지난 강좌에서 확장 메서드는 기존의 정적 헬퍼 메서드들과 다를 것이 없다고 말했었습니다. 개발하기에 상대적으로 편리하다는 것을 제외하면 말이죠(원래 작은 차이가 큰 기쁨을 주는 법이죠). 그렇다면, 이번 강좌에서는 진짜로 두 유형의 메서드가 동일한 것인지를 IL DisAssembler를 사용해서 까 발려보도록 하겠습니다.
IL DisAssembler도구는 기본적으로는 다음의 경로에 존재하고 있을 것입니다.
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\ildasm.exe
우선, 지난 강좌에서 작성한 예제 중 정적 헬퍼 메서드와 확장 메서드를 사용했던 쪽(program이라는 클래스의 Main 메서드)를 가볍게 다시 한번 바라보도록 하겠습니다.
class Program
{
static void Main(string[] args)
{
DateTime current = DateTime.Now;
DateTime dt1 = DateTimeHelper.MinusDays(current, 3);
Console.WriteLine(dt1.ToString());
DateTime dt2 = current.MinusDaysEx(3);
Console.WriteLine(dt2.ToString());
Console.ReadKey();
}
}
네. 이랬었죠.
소스 확인이 되었으면, 이번에는 .NET SDK가 기본적으로 제공하는 "IL Disassembler"란 유틸리티 도구를 사용해서 이 호출 부분(Main 메서드)이 컴파일된 IL의 코드를 째려보도록 하겠습니다. IL 언어에 익숙하지 않아도 보는 데에는 문제가 없습니다. 우리가 확인해 볼 부분은 전체 IL 코드가 아니라 정적 메서드와 확장 메서드를 호출하고 있는 코드만이니까요. 코드 중에 빨간색으로 표기된 부분이 바로 그것이죠.
.method private hidebysig static void Main( string [] args) cil managed
{
.entrypoint
// Code size 68 (0x44)
.maxstack 2
.locals init ([0] valuetype [mscorlib]System.DateTime current,
[1] valuetype [mscorlib]System.DateTime dt1,
[2] valuetype [mscorlib]System.DateTime dt2)
IL_0000: nop
IL_0001: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.3
IL_0009: call valuetype [mscorlib]System.DateTime ExtensionSample.DateTimeHelper::MinusDays(valuetype [mscorlib]System.DateTime, int32)
IL_000e: stloc.1
IL_000f: ldloca.s dt1
IL_0011: constrained. [mscorlib]System.DateTime
IL_0017: callvirt instance string [mscorlib]System.Object::ToString()
IL_001c: call void [mscorlib]System.Console::WriteLine(string)
IL_0021: nop
IL_0022: ldloc.0
IL_0023: ldc.i4.3
IL_0024: call valuetype [mscorlib]System.DateTime ExtensionSample.DateTimeHelper::MinusDaysEx(valuetype [mscorlib]System.DateTime, int32)
IL_0029: stloc.2
IL_002a: ldloca.s dt2
IL_002c: constrained. [mscorlib]System.DateTime
IL_0032: callvirt instance string [mscorlib]System.Object::ToString()
IL_0037: call void [mscorlib]System.Console::WriteLine(string)
IL_003c: nop
IL_003d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_0042: pop
IL_0043: ret
} // end of method Program::Main
어떻습니까?
놀랍지 않게도, 헬퍼 메서드를 호출하는 코드와 확장 메서드를 호출하는 코드는 내부적으로는 동일한 IL 코드임을 알 수 있습니다.
call valuetype [mscorlib]System.DateTime ExtensionSample.DateTimeHelper::MinusDays(valuetype [mscorlib]System.DateTime,
위와 같이 두 메소드의 호출 부분이 동일하다는 이야기는 DateTimeHelper 클래스에 정의된 정적 헬퍼 메서드(MinusDays)와 확장 클래스(MinusDaysEx)가 컴파일 되어 IL로 생성될 경우에 동일한 형태의 메서드로서 생성된다는 것을 의미하는 것입니다.
호오? 그렇다면 살펴보는 김에, IL Disassembler도구로 기존 예제 중 DateTimeHelper 클래스에 정의한 정적 헬퍼 메서드(MinusDays)와 확장 클래스(MinusDaysEx)의 IL을 각각 살펴보도록 하겠습니다.
[확장 메서드의 IL 코드]
[정적 헬퍼 메서드의 IL 코드]
놀랍게도, 둘의 IL 코드는 거의 동일한 것을 볼 수 있습니다. 즉, 확장 메서드나 헬퍼 메서드나 컴파일 시에는 내부적으로 동일한 유형의 IL 코드(정적 헬퍼 메서드의 유형 코드)로 변환되어 형성된다는 것입니다.
유일하게 두 IL 코드에서 차이가 있는 부분은 확장 메서드의 경우에만 존재하고 있는 다음과 같은 코드인데요.
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
이 코드가 존재함으로써, 컴파일러가 현재의 메서드가 확장 메서드임을 구분할 수 있게 됩니다. 마치, 컴파일러가 "이 메서드가 정적 헬퍼 메서드인지 확장 메서드인지 잘 모르겠지? 내가 아이스께끼 해 봤는데 확장 메서드더라고~"라고 인식할 수 있는 표기, 즉, 현재의 메서드가 확장 메서드로서 개발되었음을 나타내는 표기라고 생각하시면 될 듯 합니다.
이러 저러한 근거 자료를 통해서 복잡하게 말씀드렸지만, 정리해서 결론을 말하자면, 확장 메서드는 겉으로 보기에나 확장 메서드이지, 일단 컴파일 되고 나면 내부적으로는 정적 헬퍼 메서드와 동일하다는 것입니다.
'그렇다면, 도대체 이런 확장 메서드를 뭐하러 사용하는거야'라고 마음 속 깊이 외치시는 분이 두어 분 정도 보이십니다. 아마도 이전 강좌를 읽지 않고 오신 것 같은데요. 혈기를 조금만 누르시고 이전 강좌를 한번 보시면 마음이 편해지실 것이라 믿어 의심치 않습니다. 굳이 한 마디로 대답을 원하신다면 친절하게 말씀드릴 수도 있습니다.
"개발이 편해지잖아요!".
그리고, 이전 강좌에서도 말씀 드렸었지만, 다시 한번 강조 드리자면, 기존 형식에 존재하는 인스턴스 메서드와 동일한 형식(시그너처)으로 확장 메서드를 작성하는 경우에는 다음과 같은 우선 순위에 따라 해당 메서드가 호출됩니다.
1. 인스턴스 메서드
2. 동일 네임스페이스에 존재하는 확장 메서드
3. 현재 네임스페이스 외부에 존재하는 확장 메서드
즉, 동일한 이름의 인스턴스 메서드가 이미 존재한다면 그 녀석이 무조건 호출되고, 확장 메서드는 무시된다는 것이죠. 기억하세요. 확장 메서드는 기존에 존재하는 친구를 없애버리고 자신을 무조건 쓰게 할 정도로 막강한 파워를 가지고 있지는 못합니다. 조강지처를 빼앗을 수는 없는 녀석이 확장 메서드이다~ 그렇게 이해하시면 되겠습니다.
닷넷에서 확장 메서드가 마구 마구 사용된 예를 알려주세요! 라고 하신다면 LINQ라는 최신 기술을 한번 살펴보도록 하세요~ 라고 말씀드릴 수 있겠습니다. 그렇다면, LINQ도 알려주세요!! 라고 하신다면, 이렇게 대답을 해야 하겠죠?
'여기는 ASP.NET 사이트입니다 -_-;' 역시나 멋지게 도망가 버리죠? 우하핫~
그럼 저는 다음 강좌에서 다른 주제로 다시 찾아 뵙겠습니다~