Padrão de empacotamento de delegados
Um delegado gerenciado é empacotado como uma interface COM ou como um ponteiro de função com base no mecanismo de chamada:
Invocação de plataforma, um delegado é empacotado como um ponteiro de função não gerenciada por padrão.
Para interoperabilidade COM, um delegado é empacotado como uma interface COM do tipo _Delegate por padrão. O _Delegate interface é definida na biblioteca de tipos de Mscorlib.tlb e contém o Delegate.DynamicInvoke método, que permite que você chame o método que o delegado referências.
A tabela a seguir mostra as opções de empacotamento para o tipo de dados delegado gerenciado. O MarshalAsAttribute atributo fornece vários UnmanagedType valores de enumeração para empacotar delegados.
Tipo de enumeração |
Descrição do formato não gerenciado |
---|---|
UnmanagedType.FunctionPtr |
Um ponteiro de função não gerenciada. |
UnmanagedType.Interface |
Uma interface do tipo _Delegate, conforme definido em Mscorlib.tlb. |
Considere o seguinte exemplo de código na qual os métodos de DelegateTestInterface são exportadas para uma biblioteca de tipo COM. Observe que apenas delega marcados com o ref (ou ByRef) palavra-chave são passados como In/Out parâmetros.
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);
}
Representação de biblioteca de tipo
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);
};
Referência um ponteiro de função pode ser cancelado, assim como referência outro ponteiro de função não gerenciada pode ser cancelado.
Observação
Uma referência para o ponteiro de função para um delegado gerenciado mantido por código não gerenciado não impede que o common language runtime executar coleta de lixo no objeto gerenciado.
Por exemplo, o código a seguir está incorreto porque a referência a cb objeto passado para o SetChangeHandler método, não mantém cb alive além da vida útil da Test método. Uma vez o cb objeto é coletado pelo lixo, o ponteiro de função passado para SetChangeHandler não é mais válida.
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));
}
}
Para compensar a coleta de lixo inesperado, o chamador deve garantir que o cb objeto é mantido em atividade desde que o ponteiro de função não gerenciada está em uso. Opcionalmente, você pode fazer com que o código não gerenciado notificar o código gerenciado quando o ponteiro de função não é mais necessária, como mostra o exemplo a seguir.
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;
}
}
Consulte também
Conceitos
Blittable e tipos de não-Blittable