대리자에 대한 기본 마샬링
관리되는 대리자는 호출하는 메커니즘에 따라 COM 인터페이스 또는 함수 포인터로 마샬링됩니다.
플랫폼 호출의 경우 대리자는 기본적으로 관리되지 않는 함수 포인터로 마샬링됩니다.
COM interop의 경우 대리자는 기본적으로 _Delegate 형식의 COM 인터페이스로 마샬링됩니다. _Delegate 인터페이스는 Mscorlib.tlb 형식 라이브러리에 정의되어 있으며 대리자가 참조하는 메서드를 호출할 수 있도록 하는 Delegate.DynamicInvoke 메서드가 포함되어 있습니다.
다음 표에서는 관리되는 대리자 데이터 형식의 마샬링 옵션을 보여 줍니다. MarshalAsAttribute 특성은 대리자를 마샬링하기 위한 몇 개의 UnmanagedType 열거형 값을 제공합니다.
열거형 |
관리되지 않는 형식의 설명 |
---|---|
UnmanagedType.FunctionPtr |
관리되지 않는 함수 포인터 |
UnmanagedType.Interface |
Mscorlib.tlb에 정의된 _Delegate 형식의 인터페이스 |
다음 예제 코드에서는 DelegateTestInterface의 메서드를 COM 형식 라이브러리로 내보냅니다. 이 예제에서 ref 또는 ByRef 키워드로 표시된 대리자만 In/Out 매개 변수로 전달됩니다.
using System;
using System.Runtime.InteropServices;
public interface DelegateTest {
void m1(Delegate d);
void m2([MarshalAs(UnmanagedType.Interface)] Delegate d);
void m3([MarshalAs(UnmanagedType.Interface)] ref Delegate d);
void m4([MarshalAs(UnmanagedType.FunctionPtr)] Delegate d);
void m5([MarshalAs(UnmanagedType.FunctionPtr)] ref Delegate d);
}
형식 라이브러리 표현
importlib("mscorlib.tlb");
interface DelegateTest : IDispatch {
[id(…)] HRESULT m1([in] _Delegate* d);
[id(…)] HRESULT m2([in] _Delegate* d);
[id(…)] HRESULT m3([in, out] _Delegate** d);
[id()] HRESULT m4([in] int d);
[id()] HRESULT m5([in, out] int *d);
};
함수 포인터는 다른 관리되지 않는 함수 포인터와 마찬가지로 역참조될 수 있습니다.
참고 |
---|
비관리 코드에 포함된 관리되는 대리자에 대한 함수 포인터를 참조해도 공용 언어 런타임에서는 관리되는 개체에 대한 가비지 수집을 수행할 수 있습니다. |
예를 들어, 다음 코드에서는 cb 개체에 대한 참조가 SetChangeHandler 메서드에 전달되면 Test 메서드의 범위를 벗어나므로 cb가 유효하지 않습니다. cb 개체가 가비지 수집되면 SetChangeHandler에 전달된 함수 포인터는 더 이상 유효하지 않습니다.
public class ExternalAPI {
[DllImport("External.dll")]
public static extern void SetChangeHandler(
[MarshalAs(UnmanagedType.FunctionPtr)]ChangeDelegate d);
}
public delegate bool ChangeDelegate([MarshalAs(UnmanagedType.LPWStr) string S);
public class CallBackClass {
public bool OnChange(string S){ return true;}
}
internal class DelegateTest {
public static void Test() {
CallBackClass cb = new CallBackClass();
// Caution: The following reference on the cb object does not keep the
// object from being garbage collected after the Main method
// executes.
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
}
예기치 않은 가비지 수집 문제를 해결하기 위해 호출자는 관리되지 않는 함수 포인터가 사용되는 동안 cb 개체가 활성화 상태를 유지하는지 확인해야 합니다. 선택적으로, 함수 포인터가 더 이상 필요하지 않을 때 비관리 코드에서 관리 코드에 이를 알리도록 할 수 있습니다. 예를 들면 다음과 같습니다.
internal class DelegateTest {
CallBackClass cb;
// Called before ever using the callback function.
public static void SetChangeHandler() {
cb = new CallBackClass();
ExternalAPI.SetChangeHandler(new ChangeDelegate(cb.OnChange));
}
// Called after using the callback function for the last time.
public static void RemoveChangeHandler() {
// The cb object can be collected now. The unmanaged code is
// finished with the callback function.
cb = null;
}
}