확장 메서드
확장 메서드는 프로그래머가 정의한 정적 메서드를 인스턴스 메서드의 일부인 것처럼 사용할 수 있게 하는 기능이다.
예를 들어 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 키워드가 있어야 한다 .
주의사항
- 확장 메서드는 반드시 제네릭이 아닌 정적 클래스 위에서만 정의되어야 한다. 클래스 이름에는 제약이 없다.
- C# 컴파일러는 확장 메서드를 파일 수준에서 찾을 수 있는 정적 클래스에서만 검색한다. 즉, 다른 클래스에 중첩된 정적 클래스에 확장 메서드를 정의하려면 오류를 발생시킨다.
- 정적 클래스 이름을 자유롭게 지을 수 있기 때문에 컴파일러가 알맞는 확장 메서드를 찾는데 시간이 걸릴 수 있다. 따라서 MyListExtentions 클래스를 MyExtenstions 네임스페이스에 정의한 뒤 사용한다면 검색 성능을 개선하고 불필요한 확장 메서드는 건너 뛸 수 있다.
- 확장 메서드는 잠재적인 버전 관리 문제를 내포한다. 만약 마이크로소프트에서 나중에 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 |