언어/C#

C# 메서드 - 3. 확장 메서드

tsyang 2020. 11. 28. 19:33

확장 메서드


 

확장 메서드는 프로그래머가 정의한 정적 메서드를 인스턴스 메서드의 일부인 것처럼 사용할 수 있게 하는 기능이다.

 

예를 들어 C# 제너릭의 List 에는 요소들을 섞어주는 Suffle() 메서드가 존재하지 않는다. 이 경우 List를 매개변수로 받아 요소들을 섞어주는 메서드를 만들 수 있지만 그것보다는 List에서 직접 호출하는 편이 더 보기 좋을 것이다.

 

이 경우 다음과 같이 확장 메서드를 정의하면 된다.

using MyExtentions;

namespace MyExtentions
{
    public static class MyListExtensions
    {
        public static void Suffle<T>(this List<T> list)
        {
            // 대충 섞는 과정
        }
    }
}

public sealed class Program
{
    public static void Main()
    {
        List<int> intList = new List<int>();

        intList.Suffle();        
    }
}

 

위와 같이 정의하면 List에서 직접 Suffle을 호출할 수 있다. 

 

즉, 정적 메서드를 선언한 뒤 첫 번째 매개변수에 this를 붙이고 확장메서드를 추가할 타입을 넣으면 된다. 

 

컴파일러가 확장 메서드를 찾는 법

우선 컴파일러는 List 클래스 및 상위 클래스에 정의된 모든 인스턴스 메서드 중 매개변수가 없는 메서드가 존재하는지 검사한다. 만약 인스턴스 메서드가 있다면, 컴파일러는 해당 메서드를 호출하는 IL 코드를 만든다. 없다면 컴파일러는 첫 번째 매개변수로 호출하려는 대상 타입의 인스턴스를 받는 Suffle이라는 이름의 정적 메서드를 포함하는 정적 클래스가 있는지 확인한다. 이때 첫 번째 인자에는 반드시 this 키워드가 있어야 한다 .

 

 

주의사항

  1. 확장 메서드는 반드시 제네릭이 아닌 정적 클래스 위에서만 정의되어야 한다. 클래스 이름에는 제약이 없다.
  2. C# 컴파일러는 확장 메서드를 파일 수준에서 찾을 수 있는 정적 클래스에서만 검색한다. 즉, 다른 클래스에 중첩된 정적 클래스에 확장 메서드를 정의하려면 오류를 발생시킨다.
  3. 정적 클래스 이름을 자유롭게 지을 수 있기 때문에 컴파일러가 알맞는 확장 메서드를 찾는데 시간이 걸릴 수 있다. 따라서 MyListExtentions 클래스를 MyExtenstions 네임스페이스에 정의한 뒤 사용한다면 검색 성능을 개선하고 불필요한 확장 메서드는 건너 뛸 수 있다.
  4. 확장 메서드는 잠재적인 버전 관리 문제를 내포한다. 만약 마이크로소프트에서 나중에 List에 Suffle함수를 추가한다면 프로그래머가 정의한 확장 메서드보다 먼저 추가된 Suffle 메서드를 바인딩 할 것이고, 프로그래머의 의도와 다르게 동작할 가능성이 생긴다. (위의 컴파일러가 확장메서드를 찾는 방법 참고) 따라서 확장 메서드는 신중히 사용하는게 좋다.

 

여러 타입의 확장

다음과 같이 인터페이스 , 열거형(Enum) , 델리게이트에 대해서도 확장 메서드를 정의할 수 있다.

namespace MyExtentions
{
    public static class MyListExtensions
    {
        public static void Print<T>(this IEnumerable<T> collection) // 1. 인터페이스
        {
            foreach (var item in collection)
                System.Console.Write(item);
        }

        public static Enums.TypeB ToBType(this Enums.TypeA a) // 2. 열거형
        {
            return (Enums.TypeB)System.Enum.Parse(typeof(Enums.TypeB), a.ToString());
        }
        
        public static void InvokeAndCatch<TException>(this Action<object> d, object o) // 3. 델리게이트
            where TException : Exception
        {
            try { d(o); }
            catch (TException) { }
        }
    }
}

public sealed class Program
{
    public static void Main()
    {
        List<int> intList = new List<int>();
        intList.Print();

        Enums.TypeB a = Enums.TypeA.A.ToBType();

        Action<object> action = o => Console.WriteLine(o.GetType()); // 널익셉션 발생
        action.InvokeAndCatch<NullReferenceException>(null); // 널익셉션 무시

        Action ex = "TSYANG".Print; //특정한 객체에 대해서 확장메서드를 델리게이트로 지정할 수도 있음.
        ex();
    }
}

 

 

 

'언어 > C#' 카테고리의 다른 글

C# 매개변수  (0) 2020.12.13
C# - @ (verbatim identifier)  (2) 2020.12.07
C# 메서드 - 2. 연산자 오버로드 메서드  (0) 2020.11.28
C# 메서드 - 1. 생성자  (0) 2020.11.22
C# 의 타입 - 4. 객체의 식별 (Equals, GetHashCode)  (1) 2020.11.14