login register Sysop! about ME  
qrcode
    최초 작성일 :    2008년 08월 29일
  최종 수정일 :    2008년 08월 29일
  작성자 :    taeyo
  편집자 :    Taeyo (김 태영)
  읽음수 :    20,682

강좌 목록으로 돌아가기

필자의 잡담~

이전 강좌에서 이어서 확장 메서드 이야기입니다. 이번에는 IL을 헤집어보는 시간을 가져볼래요

지난 강좌에서 확장 메서드는 기존의 정적 헬퍼 메서드들과 다를 것이 없다고 말했었습니다. 개발하기에 상대적으로 편리하다는 것을 제외하면 말이죠(원래 작은 차이가 큰 기쁨을 주는 법이죠). 그렇다면, 이번 강좌에서는 진짜로 두 유형의 메서드가 동일한 것인지를 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 사이트입니다 -_-;' 역시나 멋지게 도망가 버리죠? 우하핫~

그럼 저는 다음 강좌에서 다른 주제로 다시 찾아 뵙겠습니다~


authored by

  soso.net
  2008-08-30(00:37)
헤헷
  alazyriver
  2008-09-03(22:06)
LINQ도 알려주세요!! 네...
  hatukoi
  2008-09-23(13:19)
제가 원래 많이 아는 녀석도 아니지만 가볍게 읽어도 무방할 정도로 편안한 글이었
습니다.도움도 많이 되고요.
그런데, 중간에 IL코드 이미지가 2장이 같은 거 같아요.혹 그런거면 수정하시면
더 멋진 강좌가 될 거 같습니다.(_ _)

  taeyo
  2008-09-23(16:33)
캐릭 이미지
감사합니다. 이미지를 올바르게 변경했습니다. ^^
  hskim618
  2009-01-12(21:05)
캐릭 이미지
확장메쏘드가 정의된 namespace명과 사용하려는 코드의 namespace명이 완전히
동일해야만 작동하네요..
다른 namespace에 확장 메쏘드를 정의해도 사용가능할 줄 알았는데 안됩니다.
이 때문에 생각보다 사용하기가 까다로와졌습니다.
제가 테스트해 본 바로는 그렇습니다. 잘못 알고 있는 거면 알려주세요..

  pangddukbaji
  2011-07-20(04:22)
캐릭 이미지
위에다가 namespace를 선언해주면 돼요... ^^

 
 
.NET과 Java 동영상 기반의 교육사이트

로딩 중입니다...

서버 프레임워크 지원 : NeoDEEX
based on ASP.NET 3.5
Creative Commons License
{5}
{2} 읽음   :{3} ({4})