Silverlight를 설치하려면 여기를 클릭합니다.*
Korea 대한민국변경|Microsoft 전체 사이트
MSDN
|개발자 센터
MSDN Home   MSDN Home
MSDN 홈 > MSDN Magazine > 2001년 기사 > 위임 소개

위임 소개


Jeffrey Richter
콜백은 지금까지 만들어진 가장 유용한 프로그래밍 메커니즘 중 하나입니다. C 런타임의 qsort 함수는 콜백 함수를 사용하여 한 배열에 요소를 정렬합니다. Windows에서 콜백 함수는 창 프로시저, 후크 프로시저, 비동기 프로시저 호출 등에 사용됩니다. 그리고 Microsoft®.NET Framework에서는 모든 작업에 콜백 메서드가 사용됩니다. 콜백 메서드를 등록하면 어셈블리 로드/언로드 통지, 처리되지 않은 예외 통지, 데이터베이스/창 상태 변경 통지, 파일 시스템 변경 통지, 메뉴 항목 선택, 완료된 비동기 실행 통지, 일련의 항목 필터링 등의 작업을 할 수 있습니다.

C/C++에서 함수 주소는 단지 메모리 주소일 뿐이며, 이 주소에는 해당 함수에서 기대하는 매개 변수의 수, 이러한 매개 변수 유형, 해당 함수의 반환 값 유형, 함수의 호출 변환 등 추가 정보는 들어있지 않습니다. 즉, C/C++ 호출 함수는 타입-세이프가 아닙니다.

.NET Framework에서는 Windows®용으로 관리되지 않는 프로그래밍에 이 콜백 함수가 있으므로 유용하며 널리 사용됩니다. 하지만 .NET Framework에는 위임이라는 이름의 타입-세이프 메커니즘을 제공하는 추가 보너스가 있습니다. 위임 사용 방법을 통해 위임에 대해 설명하겠습니다. 그림?1의 코드를 보면 위임을 선언하고 만들고 사용하는 방법을 알 수 있습니다.

그림?1의 맨 위에 Set 클래스가 있습니다. 이 클래스에는 개별적으로 처리될 항목 집합이 들어 있다고 가정합시다. Set 개체를 만들 때에는 해당 개체가 관리해야 하는 항목의 수를 생성자에게 전달합니다. 그러면 생성자가 Object 배열을 만들고 각각을 정수 값으로 초기화합니다.

Set 클래스는 공개 위임도 정의합니다. 이 위임에는 호출 메서드의 서명이 표시됩니다. 이 예에서 Feedback 위임은 세 개의 매개 변수(Object, Int32, 또 다른 Int32)를 필요로 하는 메서드를 확인하고 공백을 반환합니다. 어떤 면에서는 위임은 함수의 주소를 표시하는 C/C++ Typedef와 상당히 비슷합니다.

이 외에도 Set 클래스는 ProcessItems라는 공개 메서드를 정의합니다. 이 메서드는 피드백이라는 하나의 매개 변수를 필요로 합니다. 이 매개 변수는 Feedback 위임 개체에 대한 참조입니다. ProcessItems는 한 배열에 속한 모든 요소를 반복 실행하며 개별 요소마다 콜백 메서드(이 feedback 변수에 지정됨)를 호출합니다. 이 콜백 메서드에는 처리 중인 항목 값, 항목 번호, 배열에 속한 모든 항목의 수가 전달되며, 각 항목을 선택한 방향으로 처리할 수 있습니다.

맨 위로


위임을 사용한 정적 메서드 호출

StaticCallbacks 메서드는 콜백 위임을 사용하는 다양한 방법을 실제로 보여줍니다. 이 메서드는 Set 개체를 만드는 것으로 시작하여 개체 5개로 이루어진 배열을 만들도록 합니다. 그리고 ProcessItems를 호출하여 피드백 매개 변수에 널을 전달합니다. 이것이 위임을 사용하는 첫 번째 예입니다. ProcessItems는 Set이 관리하는 모든 항목에 대해 일부 작업을 실행하는 메서드를 나타냅니다. 이 예에서는 피드백 매개 변수가 널이므로 콜백 메서드를 호출하지 않아도 각 항목이 처리됩니다.

두 번째 예에서는, 새 Set.Feedback 위임 개체를 만듭니다. 이 위임 개체는 한 메서드를 둘러싸는 래퍼로, 이 래퍼를 통해 메서드를 직접 호출할 수 있도록 해 줍니다. Feedback 유형의 생성자에게 메서드 이름 즉, 이 예에서는 App.FeedbackToConsole이 전달됩니다. 이는 이 메서드가 래핑되었다는 것을 뜻합니다. 그러면 이 새 운영자에게서 반환된 참조가 ProcessItems로 전달됩니다. 이제 ProcessItems가 실행되면 이 집합의 개별 항목에 대해 App 유형의 FeedbackToConsole 메서드가 호출됩니다. FeedbackToConsole은 처리 중인 항목과 해당 항목의 값을 표시하는 문자열을 콘솔에 씁니다.

세 번째 예는 두 번째 예와 거의 비슷합니다. 유일한 차이점이라면 Feedback 위임 개체가 App.FeedbackToMsgBox 메서드를 래핑한다는 것입니다. FeedbackToMsgBox는 처리 중인 항목과 해당 항목의 값을 표시하는 문자열을 만듭니다. 그리고 이 문자열이 메시지 상자에 표시됩니다.

네 번째와 마지막 예는 위임을 연결하여 체인을 만드는 방법에 대한 것입니다. 이 예에서는 Feedback 위임 개체에 대한 참조 개체인 fb가 만들어지며 널로 초기화됩니다. 이 변수는 연결된 위임 목록의 맨 위를 표시합니다. 널 값은 연결된 목록에 노드가 없다는 것을 뜻합니다. 그러면 App의 FeedbackToConsole 메서드 호출을 래핑하는 Feedback 위임 개체가 만들어 집니다. C# += 운영자를 사용하여 이 개체를 fb가 참조하는 연결된 목록에 덧붙입니다. 이제 fb 변수는 연결된 목록의 맨 위를 참조합니다.

마지막으로, App의 FeedbackToMsgBox 메서드 호출을 래핑하는 또 다른 Feedback 위임 개체를 만드는 예입니다. 여기서도 C# += 운영자는 이 개체를 연결된 목록에 첨부하는 데 사용되며 연결된 목록의 맨 위를 참조하도록 fb가 업데이트됩니다. 이제, ProcessItems가 호출되면 Feedback 위임의 연결된 목록의 맨 위로 전달됩니다. ProcessItems 내에서는 콜백 메서드를 호출하는 코드 줄이 실제로 연결된 목록의 위임 개체가 래핑하는 모든 콜백 메서드를 호출합니다. 다시 말해, 반복 적용되는 각 항목에 대해 FeedbackToConsole이 호출되며 그 뒤에 FeedbackToMsgBox가 따라옵니다. 다음 컬럼에서는 위임 체인이 어떻게 실행되는지를 다루겠습니다.

이 예에서는 모든 것이 타입-세이프라는 것에 주의해야 합니다. 예를 들어, Feedback 위임 개체를 만들 때 컴파일러는 App의 FeedbackToConsole과 FeedbackToMsgBox 메서드에 Feedback 위임에서 정의한 정확한 프로토타입이 있는지 확인합니다. 즉, 두 메서드는 세 개의 매개 변수(Object, Int32, Int32)를 필요로 하며 두 메서드는 반드시 동일한 반환 유형(공백)을 가져야 합니다. 이 메서드 프로토타입이 일치하지 않으면 컴파일러가 다음 오류 메시지를 나타냅니다: "error CS0123: The signature of method 'App.FeedbackToMsgBox()' does not match this delegate type."

맨 위로


인스턴스 메서드 호출

지금까지 위임을 사용해 정적 메서드를 호출하는 방법을 설명했습니다. 하지만 특정 개체에 대한 인스턴스 메서드를 호출할 때에도 위임이 사용됩니다. 인스턴스 메서드의 경우 위임은 이 메서드가 실행하는 개체의 인스턴스를 알아야 합니다.

인스턴스 메서드 호출이 어떻게 실행되는지를 이해하려면 그림?1의 InstanceCallbacks 메서드를 살펴 보십시오. 이 코드는 정적 메서드의 코드와 아주 유사합니다. Set 개체가 만들어진 후 App 개체가 만들어진다는 사실에 주의하십시오. 이 App 개체에는 관련 필드나 속성이 없으며 데모 용으로만 만들어집니다. 새 Feedback 위임 개체가 만들어지면 생성자에 appobj.FeedbackToFile이 전달됩니다. 이렇게 되면 이 위임이 참조를 인스턴스 메서드인 FeedbackToFile 메서드(정적이지 않음)로 래핑합니다. 이 인스턴스 메서드가 호출되면 appobj가 참조하는 개체는 실행 중인 개체가 됩니다(숨겨진 이 매개 변수로 전달). FeedbackToFile 메서드는 파일을 열고 처리 항목을 파일 끝에 붙인다는 점을 제외하고는 FeedbackToConsole 및 FeedbackToMsgBox 메서드와 비슷하게 실행됩니다.

맨 위로


위임 제거

표면적으로 보면 위임은 정말 사용이 간편합니다. C# 위임 키워드를 사용하여 위임을 정의한 다음 익숙한 새 운영자를 사용하여 위임 인스턴스를 만들고 익숙한 메서드 호출 구문(메서드 이름 대신 위임 개체를 참조하는 변수를 사용한다는 점만 다름)을 사용하여 이 콜백을 호출합니다.

하지만 실제로는 앞 예에서 설명한 것보다는 조금 더 복잡합니다. 이 컴파일러와 CLR(common language runtime)은 많은 백그라운드 처리를 통해 이러한 복잡성을 숨겨 줍니다. 여기서는 컴파일러와 CLR이 어떻게 위임을 실행하는지를 중심으로 설명하겠습니다. 이 실행 방법을 알면 위임에 대한 이해가 높아지므로 위임을 효과적이고 효율적으로 이용하는 데 도움이 될 것입니다. 그리고 사용자 코드에서 위임을 사용할 수 있도록 하는 추가 기능도 설명합니다.

먼저 코드의 다음 줄을 살펴 봅시다.
public delegate void Feedback(
   Object value, Int32 item, Int32 numItems);
컴파일러는 이전 줄을 볼 때 그림?2의 코드와 유사한 완벽한 클래스 정의를 정확하게 정의합니다.
ILDasm.exe를 사용해 결과 모듈을 검사해 보면 이 컴파일러가 자동으로 이 클래스를 만들었다는 것을 확인할 수 있습니다(그림?3 참조).

이 예에서 컴파일러는 System.MulticastDelegate 유형에서 유도된 Feedback이라는 클래스를 정의하였으며 이 유형은 Framework Class Library에서 정의됩니다. 위임 유형은 모두 MulticastDelegate에서 유도된다는 점을 기억하십시오. 이 예에서 보면 원본 코드에서 위임이 공용으로 선언되었으므로 Feedback 클래스는 공용입니다. 이 원본이 개인 또는 보호로 표시되어 있다면 이 컴파일러가 생성하는 Feedback 클래스도 개인 또는 보호가 됩니다. 위임 유형은 클래스 안에서 정의되며(예와 같이 Feedback은 Set 클래스 내에서 정의됨) 위임은 글로벌 범위에서 정의됩니다. 기본적으로 위임은 클래스이므로 클래스가 정의되는 곳이라면 어디에서나 위임이 정의됩니다.

모든 위임 유형은 MulticastDelegate에서 유도되므로 이 유형은 MulticastDelegate의 필드, 속성, 메서드를 상속합니다. 모든 구성원 중 반드시 알아야 하는 개인 필드가 셋 있습니다(그림?4 참조).

모든 위임에는 생성자가 있으며 각 생성자는 두 개의 매개 변수 즉, 한 개체에 대한 참조와 콜백 메서드를 참조하는 정수를 갖습니다. 하지만 원본 코드를 살펴보면 App.FeedbackToConsole 또는 appobj.FeedbackToFile과 같은 값 내에서 전달한다는 것을 알 수 있습니다. 이 코드는 컴파일해서는 안 된다는 것은 말하지 않아도 알 수 있을 것입니다!

하지만 이 컴파일러는 위임이 만들어지고 있다는 것을 알며, 원본 코드를 분석하여 어떤 개체와 메서드가 참조되고 있는지를 확인합니다. 그런 다음 대상 매개 변수에 대한 개체 참조가 전달되며 methodPtr 매개 변수에 대해 해당 메서드를 확인하는 Int32 값(MethodDef 또는 MethodRef 메타데이터 토큰에서 얻음)이 전달됩니다. 정적 메서드의 경우에는 대상 매개 변수에 대해 널이 전달됩니다. 생성자 내부의 해당 개인 필드에 이 두 매개 변수가 저장됩니다.

이 외에도 생성자가 이 필드를 널로 설정합니다. 이 필드는 MulticastDelegate 개체의 연결되는 목록을 만드는 데 사용됩니다. _prev 필드는 여기서는 무시하고 다음 .NET 컬럼에서 자세히 설명하겠습니다.

각 위임 개체는 실제로 메서드를 둘러싸는 래퍼로, 메서드가 호출될 때 실행됩니다. MulticastDelegate 클래스는 두 개의 읽기 전용 공용 인스턴스 속성 Target과 Method를 정의합니다. 위임 개체에 대한 참조를 고려하면 이러한 속성을 쿼리할 수 있습니다. Target 속성은 이 메서드가 콜백되면 실행되는 개체에 참조를 반환합니다. 이 메서드가 정적이면 Target은 널을 반환합니다. Method 속성은 콜백 메서드를 확인하는 System.Reflection.MethodInfo 개체를 반환합니다.

이 정보를 사용할 수 있는 방법이 몇 가지 있습니다. 한 가지는 위임 개체가 특정 유형의 인스턴스 메서드를 참조하는지를 확인하는 방법입니다.
Boolean DelegateRefersToInstanceMethodOfType(
   MulticastDelegate d, Type type) {

   return((d.Target != null) && d.Target.GetType == type);
}
콜백 메서드에 특정한 이름(예: FeedbackToMsgBox)이 있는지를 확인하는 코드도 작성할 수 있습니다.
Boolean DelegateRefersToMethodOfName(
   MulticastDelegate d, String methodName) {

   return(d.Method.Name == methodName);
}
이제 위임 개체가 어떻게 구성되는지를 알았으므로 콜백 메서드가 실제로 어떻게 호출되는지를 알아 봅시다. 편의상 Set의 ProcessItems 코드를 다시 이용해 보겠습니다.
public void ProcessItems(Feedback feedback) {
   for (Int32 item = 1; item <= items.Length; item++) {
      if (feedback != null) {
         // If any callbacks are specified, call them
         feedback(items[item], item, items.Length);
      }
   }
}
이 설명 바로 아래가 콜백 메서드를 호출하는 코드 줄입니다. 자세히 살펴보면 제가 실제로 feedback 함수를 호출하고 이 함수에 세 개의 매개 변수를 전달하는 것으로 보입니다. 하지만 feedback이라는 함수는 없습니다. 여기서도 컴파일러는 피드백이 위임 개체를 참조하는 변수라는 것을 알고 있으며 컴파일러는 실제로 위임 개체의 Invoke 메서드를 호출하는 코드를 만듭니다. 다시 말해 컴파일러는 다음 내용을 알고 있지만
feedback(items[item], item, items.Length);
이 원본 코드가 다음과 같이 생성됩니다.
feedback.Invoke(items[item], item, items.Length);
사실, ILDasm.exe를 사용하여 ProcessItems 메서드에 대한 코드를 확인하면 이를 확인할 수 있습니다.

그림?5는 Set 유형의 ProcessItems 메서드에 대한 중간 언어입니다. 빨간색 화살표는 Set.Feedback의 Invoke 메서드를 호출하는 설명을 표시합니다. 원본 코드를 변경하여 Invoke를 명시적으로 호출하려고 하면 C# 컴파일러가 "error CS1533: Invoke cannot be called directly on a delegate" 오류 메시지를 생성합니다. C#에서는 명시적으로 Invoke를 호출할 수 없습니다. 하지만 다른 컴파일러는 호출할 수 있을 수도 있습니다.

Figure 5 SetProcessItems (IL disassembly)
그림 5 SetProcessItems(IL 디스어셈블리)

컴파일러가 Feedback 클래스를 정의할 때 Invoke 메서드도 정의했다는 것을 알아두십시오. Invoke가 호출되면 private _target와 _methodPtr 필드를 사용하여 지정된 개체에서 원하는 메서드를 호출합니다. Invoke 메서드의 서명은 위임의 서명과 정확하게 일치합니다. 즉, Feedback 위임에 매개 변수가 셋이며 공백을 반환하므로 Invoke 메서드도 똑같이 세개의 매개 변수를 필요로 하고 공백을 반환합니다.

맨 위로


결론

이번 컬럼에서는 지면 부족으로 위임에 대해 더 이상 설명을 진행하기가 어렵습니다. 그러나 이 정도면 위임을 만들고 사용하는 방법 정도는 충분히 설명되었을 것으로 생각합니다. 다음 컬럼에서는 연결된 목록 위임 체인, MulticastDelegate의 추가 메서드, System.Delegate 유형, 그리고 이벤트에 대해 설명하겠습니다. 그때까지는 제게 연락하지 마세요. 제가 연락 드리겠습니다!

Jeffrey RichterProgramming Applications for Microsoft Windows(Microsoft Press, 1999)의 저자이며 소프트웨어 교육, 디버깅 및 컨설팅 회사인 Wintellect(http://www.wintellect.com/) 공동 설립자입니다. .NET과 Win32의 프로그래밍/디자인이 전공이며, 현재 Microsoft .NET Framework 프로그래밍 책을 집필 중이고 .NET 기술 세미나를 진행하고 있습니다.


? 최종수정일: 2001년 4월 26일

Top of Page Top of Page


Microsoft