Guide pratique pour raccorder un délégué à l’aide de la réflexion
Quand vous utilisez la réflexion pour charger et exécuter des assemblys, vous ne pouvez pas utiliser des fonctionnalités de langage telles que l’opérateur C# +=
ou l’instruction AddHandler Visual Basic pour raccorder des événements. Les procédures suivantes montrent comment raccorder une méthode existante à un événement en obtenant tous les types nécessaires par réflexion, et comment créer une méthode dynamique à l’aide de l’émission de réflexion et la raccorder à un événement.
Notes
Pour découvrir une autre manière de raccorder un délégué de gestion des événements, consultez l’exemple de code relatif à la méthode AddEventHandler de la classe EventInfo.
Pour raccorder un délégué à l’aide de la réflexion
Chargez un assembly qui contient un type qui déclenche des événements. Les assemblys sont habituellement chargés avec la méthode Assembly.Load. Pour simplifier cet exemple, un formulaire dérivé dans l’assembly actuel est utilisé. La méthode GetExecutingAssembly est donc utilisée pour charger l’assembly actuel.
Assembly^ assem = Example::typeid->Assembly;
Assembly assem = typeof(Example).Assembly;
Dim assem As Assembly = GetType(Example).Assembly
Obtenez un objet Type représentant le type et créez une instance du type. La méthode CreateInstance(Type) est utilisée dans le code suivant car le formulaire a un constructeur sans paramètre. Vous pouvez utiliser plusieurs autres surcharges de la méthode CreateInstance si le type que vous créez n’a pas de constructeur sans paramètre. La nouvelle instance est stockée en tant que type Object pour conserver l’illusion que l’assembly est inconnu. (La réflexion vous autorise à obtenir les types dans un assembly sans connaître leurs noms à l’avance.)
Type^ tExForm = assem->GetType("ExampleForm"); Object^ exFormAsObj = Activator::CreateInstance(tExForm);
Type tExForm = assem.GetType("ExampleForm"); Object exFormAsObj = Activator.CreateInstance(tExForm);
Dim tExForm As Type = assem.GetType("ExampleForm") Dim exFormAsObj As Object = _ Activator.CreateInstance(tExForm)
Obtenez un objet EventInfo représentant l’événement, et utilisez la propriété EventHandlerType pour obtenir le type de délégué utilisé pour gérer l’événement. Dans le code suivant, un EventInfo pour l’événement Click est obtenu.
EventInfo^ evClick = tExForm->GetEvent("Click"); Type^ tDelegate = evClick->EventHandlerType;
EventInfo evClick = tExForm.GetEvent("Click"); Type tDelegate = evClick.EventHandlerType;
Dim evClick As EventInfo = tExForm.GetEvent("Click") Dim tDelegate As Type = evClick.EventHandlerType
Obtenez un objet MethodInfo représentant la méthode qui gère l’événement. Le code complet du programme dans la section Exemple plus loin dans cet article contient une méthode qui correspond à la signature du délégué EventHandler, qui gère l’événement Click, mais vous pouvez également générer des méthodes dynamiques au moment de l’exécution. Pour plus d’informations, consultez la procédure associée expliquant comment générer un gestionnaire d’événements au moment de l’exécution à l’aide d’une méthode dynamique.
MethodInfo^ miHandler = Type::GetType("Example")->GetMethod("LuckyHandler", BindingFlags::NonPublic | BindingFlags::Instance);
MethodInfo miHandler = typeof(Example).GetMethod("LuckyHandler", BindingFlags.NonPublic | BindingFlags.Instance);
Dim miHandler As MethodInfo = _ GetType(Example).GetMethod("LuckyHandler", _ BindingFlags.NonPublic Or BindingFlags.Instance)
Créez une instance du délégué à l’aide de la méthode CreateDelegate. Cette méthode étant statique (
Shared
en Visual Basic), vous devez fournir le type délégué. Nous vous recommandons d’utiliser les surcharges de CreateDelegate qui prennent un MethodInfo.Delegate^ d = Delegate::CreateDelegate(tDelegate, this, miHandler);
Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
Dim d As [Delegate] = _ [Delegate].CreateDelegate(tDelegate, Me, miHandler)
Obtenez la méthode d’accesseur
add
et appelez-la pour raccorder l’événement. Tous les événements ont un accesseuradd
et un accesseurremove
, qui sont masqués par la syntaxe des langages de haut niveau. Par exemple, C# utilise l’opérateur+=
pour raccorder des événements, et Visual Basic utilise l’instruction AddHandler. Le code suivant obtient l’accesseuradd
de l’événement Click et l’appelle avec liaison tardive, en passant l’instance de délégué. Les arguments doivent être passés sous forme de tableau.MethodInfo^ addHandler = evClick->GetAddMethod(); array<Object^>^ addHandlerArgs = { d }; addHandler->Invoke(exFormAsObj, addHandlerArgs);
MethodInfo addHandler = evClick.GetAddMethod(); Object[] addHandlerArgs = { d }; addHandler.Invoke(exFormAsObj, addHandlerArgs);
Dim miAddHandler As MethodInfo = evClick.GetAddMethod() Dim addHandlerArgs() As Object = {d} miAddHandler.Invoke(exFormAsObj, addHandlerArgs)
Testez l’événement. Le code suivant montre le formulaire défini dans l’exemple de code. Quand vous cliquez sur le formulaire, le gestionnaire d’événements est appelé.
Application::Run((Form^) exFormAsObj);
Application.Run((Form) exFormAsObj);
Application.Run(CType(exFormAsObj, Form))
Générez un gestionnaire d’événements au moment de l’exécution en utilisant une méthode dynamique
Vous pouvez générer des méthodes de gestionnaire d’événements au moment de l’exécution, à l’aide de méthodes dynamiques légères et de l’émission de réflexion. Pour créer un gestionnaire d’événements, vous avez besoin du type de retour et des types de paramètres du délégué. Vous pouvez les obtenir en examinant la méthode
Invoke
du délégué. Le code suivant utilise les méthodesGetDelegateReturnType
etGetDelegateParameterTypes
pour obtenir ces informations. Vous trouverez le code de ces méthodes dans la section Exemple plus loin dans cet artticle.Comme il n’est pas nécessaire de nommer un DynamicMethod, la chaîne vide peut être utilisée. Dans le code suivant, le dernier argument associe la méthode dynamique au type actuel, ce qui donne au délégué accès à tous les membres privés et publics de la classe
Example
.Type^ returnType = GetDelegateReturnType(tDelegate); if (returnType != void::typeid) throw gcnew ApplicationException("Delegate has a return type."); DynamicMethod^ handler = gcnew DynamicMethod("", nullptr, GetDelegateParameterTypes(tDelegate), Example::typeid);
Type returnType = GetDelegateReturnType(tDelegate); if (returnType != typeof(void)) throw new ArgumentException("Delegate has a return type.", nameof(d)); DynamicMethod handler = new DynamicMethod("", null, GetDelegateParameterTypes(tDelegate), typeof(Example));
Dim returnType As Type = GetDelegateReturnType(tDelegate) If returnType IsNot GetType(Void) Then Throw New ArgumentException("Delegate has a return type.", NameOf(d)) End If Dim handler As New DynamicMethod( _ "", _ Nothing, _ GetDelegateParameterTypes(tDelegate), _ GetType(Example) _ )
Générez un corps de méthode. Cette méthode charge une chaîne, appelle la surcharge de la méthode MessageBox.Show qui prend une chaîne, dépile la valeur de retour (puisque le gestionnaire n’a aucun type de retour) et retourne. Pour en savoir plus sur l’émission de méthodes dynamiques, consultez Guide pratique pour définir et exécuter des méthodes dynamiques.
ILGenerator^ ilgen = handler->GetILGenerator(); array<Type^>^ showParameters = { String::typeid }; MethodInfo^ simpleShow = MessageBox::typeid->GetMethod("Show", showParameters); ilgen->Emit(OpCodes::Ldstr, "This event handler was constructed at run time."); ilgen->Emit(OpCodes::Call, simpleShow); ilgen->Emit(OpCodes::Pop); ilgen->Emit(OpCodes::Ret);
ILGenerator ilgen = handler.GetILGenerator(); Type[] showParameters = { typeof(String) }; MethodInfo simpleShow = typeof(MessageBox).GetMethod("Show", showParameters); ilgen.Emit(OpCodes.Ldstr, "This event handler was constructed at run time."); ilgen.Emit(OpCodes.Call, simpleShow); ilgen.Emit(OpCodes.Pop); ilgen.Emit(OpCodes.Ret);
Dim ilgen As ILGenerator = handler.GetILGenerator() Dim showParameters As Type() = {GetType(String)} Dim simpleShow As MethodInfo = _ GetType(MessageBox).GetMethod("Show", showParameters) ilgen.Emit(OpCodes.Ldstr, _ "This event handler was constructed at run time.") ilgen.Emit(OpCodes.Call, simpleShow) ilgen.Emit(OpCodes.Pop) ilgen.Emit(OpCodes.Ret)
Terminez l’exécution de la méthode dynamique en appelant sa méthode CreateDelegate. Utilisez l’accesseur
add
pour ajouter le délégué à la liste d’appels de l’événement.Delegate^ dEmitted = handler->CreateDelegate(tDelegate); addHandler->Invoke(exFormAsObj, gcnew array<Object^> { dEmitted });
Delegate dEmitted = handler.CreateDelegate(tDelegate); addHandler.Invoke(exFormAsObj, new Object[] { dEmitted });
Dim dEmitted As [Delegate] = handler.CreateDelegate(tDelegate) miAddHandler.Invoke(exFormAsObj, New Object() {dEmitted})
Testez l’événement. Le code suivant charge le formulaire défini dans l’exemple de code. Quand vous cliquez sur le formulaire, le gestionnaire d’événements prédéfini et le gestionnaire d’événements émis sont appelés.
Application::Run((Form^) exFormAsObj);
Application.Run((Form) exFormAsObj);
Application.Run(CType(exFormAsObj, Form))
Exemple
L’exemple de code suivant montre comment raccorder une méthode existante à un événement à l’aide de la réflexion, et également comment utiliser la classe DynamicMethod pour émettre une méthode au moment de l’exécution et la raccorder à un événement.
#using <System.dll>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Reflection;
using namespace System::Reflection::Emit;
using namespace System::Windows::Forms;
public ref class ExampleForm : public Form
{
public:
ExampleForm() : Form()
{
this->Text = "Click me";
}
};
public ref class Example
{
public:
static void Main()
{
Example^ ex = gcnew Example();
ex->HookUpDelegate();
}
private:
void HookUpDelegate()
{
// Load an assembly, for example using the Assembly.Load
// method. In this case, the executing assembly is loaded, to
// keep the demonstration simple.
//
Assembly^ assem = Example::typeid->Assembly;
// Get the type that is to be loaded, and create an instance
// of it. Activator::CreateInstance has other overloads, if
// the type lacks a default constructor. The new instance
// is stored as type Object, to maintain the fiction that
// nothing is known about the assembly. (Note that you can
// get the types in an assembly without knowing their names
// in advance.)
//
Type^ tExForm = assem->GetType("ExampleForm");
Object^ exFormAsObj = Activator::CreateInstance(tExForm);
// Get an EventInfo representing the Click event, and get the
// type of delegate that handles the event.
//
EventInfo^ evClick = tExForm->GetEvent("Click");
Type^ tDelegate = evClick->EventHandlerType;
// If you already have a method with the correct signature,
// you can simply get a MethodInfo for it.
//
MethodInfo^ miHandler =
Type::GetType("Example")->GetMethod("LuckyHandler",
BindingFlags::NonPublic | BindingFlags::Instance);
// Create an instance of the delegate. Using the overloads
// of CreateDelegate that take MethodInfo is recommended.
//
Delegate^ d = Delegate::CreateDelegate(tDelegate, this, miHandler);
// Get the "add" accessor of the event and invoke it late-
// bound, passing in the delegate instance. This is equivalent
// to using the += operator in C#, or AddHandler in Visual
// Basic. The instance on which the "add" accessor is invoked
// is the form; the arguments must be passed as an array.
//
MethodInfo^ addHandler = evClick->GetAddMethod();
array<Object^>^ addHandlerArgs = { d };
addHandler->Invoke(exFormAsObj, addHandlerArgs);
// Event handler methods can also be generated at run time,
// using lightweight dynamic methods and Reflection.Emit.
// To construct an event handler, you need the return type
// and parameter types of the delegate. These can be obtained
// by examining the delegate's Invoke method.
//
// It is not necessary to name dynamic methods, so the empty
// string can be used. The last argument associates the
// dynamic method with the current type, giving the delegate
// access to all the public and private members of Example,
// as if it were an instance method.
//
Type^ returnType = GetDelegateReturnType(tDelegate);
if (returnType != void::typeid)
throw gcnew ApplicationException("Delegate has a return type.");
DynamicMethod^ handler =
gcnew DynamicMethod("",
nullptr,
GetDelegateParameterTypes(tDelegate),
Example::typeid);
// Generate a method body. This method loads a string, calls
// the Show method overload that takes a string, pops the
// return value off the stack (because the handler has no
// return type), and returns.
//
ILGenerator^ ilgen = handler->GetILGenerator();
array<Type^>^ showParameters = { String::typeid };
MethodInfo^ simpleShow =
MessageBox::typeid->GetMethod("Show", showParameters);
ilgen->Emit(OpCodes::Ldstr,
"This event handler was constructed at run time.");
ilgen->Emit(OpCodes::Call, simpleShow);
ilgen->Emit(OpCodes::Pop);
ilgen->Emit(OpCodes::Ret);
// Complete the dynamic method by calling its CreateDelegate
// method. Use the "add" accessor to add the delegate to
// the invocation list for the event.
//
Delegate^ dEmitted = handler->CreateDelegate(tDelegate);
addHandler->Invoke(exFormAsObj, gcnew array<Object^> { dEmitted });
// Show the form. Clicking on the form causes the two
// delegates to be invoked.
//
Application::Run((Form^) exFormAsObj);
}
void LuckyHandler(Object^ sender, EventArgs^ e)
{
MessageBox::Show("This event handler just happened to be lying around.");
}
array<Type^>^ GetDelegateParameterTypes(Type^ d)
{
if (d->BaseType != MulticastDelegate::typeid)
throw gcnew ApplicationException("Not a delegate.");
MethodInfo^ invoke = d->GetMethod("Invoke");
if (invoke == nullptr)
throw gcnew ApplicationException("Not a delegate.");
array<ParameterInfo^>^ parameters = invoke->GetParameters();
array<Type^>^ typeParameters = gcnew array<Type^>(parameters->Length);
for (int i = 0; i < parameters->Length; i++)
{
typeParameters[i] = parameters[i]->ParameterType;
}
return typeParameters;
}
Type^ GetDelegateReturnType(Type^ d)
{
if (d->BaseType != MulticastDelegate::typeid)
throw gcnew ApplicationException("Not a delegate.");
MethodInfo^ invoke = d->GetMethod("Invoke");
if (invoke == nullptr)
throw gcnew ApplicationException("Not a delegate.");
return invoke->ReturnType;
}
};
int main()
{
Example::Main();
}
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Windows.Forms;
class ExampleForm : Form
{
public ExampleForm() : base()
{
this.Text = "Click me";
}
}
class Example
{
public static void Main()
{
Example ex = new Example();
ex.HookUpDelegate();
}
private void HookUpDelegate()
{
// Load an assembly, for example using the Assembly.Load
// method. In this case, the executing assembly is loaded, to
// keep the demonstration simple.
//
Assembly assem = typeof(Example).Assembly;
// Get the type that is to be loaded, and create an instance
// of it. Activator.CreateInstance has other overloads, if
// the type lacks a default constructor. The new instance
// is stored as type Object, to maintain the fiction that
// nothing is known about the assembly. (Note that you can
// get the types in an assembly without knowing their names
// in advance.)
//
Type tExForm = assem.GetType("ExampleForm");
Object exFormAsObj = Activator.CreateInstance(tExForm);
// Get an EventInfo representing the Click event, and get the
// type of delegate that handles the event.
//
EventInfo evClick = tExForm.GetEvent("Click");
Type tDelegate = evClick.EventHandlerType;
// If you already have a method with the correct signature,
// you can simply get a MethodInfo for it.
//
MethodInfo miHandler =
typeof(Example).GetMethod("LuckyHandler",
BindingFlags.NonPublic | BindingFlags.Instance);
// Create an instance of the delegate. Using the overloads
// of CreateDelegate that take MethodInfo is recommended.
//
Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
// Get the "add" accessor of the event and invoke it late-
// bound, passing in the delegate instance. This is equivalent
// to using the += operator in C#, or AddHandler in Visual
// Basic. The instance on which the "add" accessor is invoked
// is the form; the arguments must be passed as an array.
//
MethodInfo addHandler = evClick.GetAddMethod();
Object[] addHandlerArgs = { d };
addHandler.Invoke(exFormAsObj, addHandlerArgs);
// Event handler methods can also be generated at run time,
// using lightweight dynamic methods and Reflection.Emit.
// To construct an event handler, you need the return type
// and parameter types of the delegate. These can be obtained
// by examining the delegate's Invoke method.
//
// It is not necessary to name dynamic methods, so the empty
// string can be used. The last argument associates the
// dynamic method with the current type, giving the delegate
// access to all the public and private members of Example,
// as if it were an instance method.
//
Type returnType = GetDelegateReturnType(tDelegate);
if (returnType != typeof(void))
throw new ArgumentException("Delegate has a return type.", nameof(d));
DynamicMethod handler =
new DynamicMethod("",
null,
GetDelegateParameterTypes(tDelegate),
typeof(Example));
// Generate a method body. This method loads a string, calls
// the Show method overload that takes a string, pops the
// return value off the stack (because the handler has no
// return type), and returns.
//
ILGenerator ilgen = handler.GetILGenerator();
Type[] showParameters = { typeof(String) };
MethodInfo simpleShow =
typeof(MessageBox).GetMethod("Show", showParameters);
ilgen.Emit(OpCodes.Ldstr,
"This event handler was constructed at run time.");
ilgen.Emit(OpCodes.Call, simpleShow);
ilgen.Emit(OpCodes.Pop);
ilgen.Emit(OpCodes.Ret);
// Complete the dynamic method by calling its CreateDelegate
// method. Use the "add" accessor to add the delegate to
// the invocation list for the event.
//
Delegate dEmitted = handler.CreateDelegate(tDelegate);
addHandler.Invoke(exFormAsObj, new Object[] { dEmitted });
// Show the form. Clicking on the form causes the two
// delegates to be invoked.
//
Application.Run((Form) exFormAsObj);
}
private void LuckyHandler(Object sender, EventArgs e)
{
MessageBox.Show("This event handler just happened to be lying around.");
}
private Type[] GetDelegateParameterTypes(Type d)
{
if (d.BaseType != typeof(MulticastDelegate))
throw new ArgumentException("Not a delegate.", nameof(d));
MethodInfo invoke = d.GetMethod("Invoke");
if (invoke == null)
throw new ArgumentException("Not a delegate.", nameof(d));
ParameterInfo[] parameters = invoke.GetParameters();
Type[] typeParameters = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
typeParameters[i] = parameters[i].ParameterType;
}
return typeParameters;
}
private Type GetDelegateReturnType(Type d)
{
if (d.BaseType != typeof(MulticastDelegate))
throw new ArgumentException("Not a delegate.", nameof(d));
MethodInfo invoke = d.GetMethod("Invoke");
if (invoke == null)
throw new ArgumentException("Not a delegate.", nameof(d));
return invoke.ReturnType;
}
}
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Windows.Forms
Class ExampleForm
Inherits Form
Public Sub New()
Me.Text = "Click me"
End Sub
End Class
Class Example
Public Shared Sub Main()
Dim ex As New Example()
ex.HookUpDelegate()
End Sub
Private Sub HookUpDelegate()
' Load an assembly, for example using the Assembly.Load
' method. In this case, the executing assembly is loaded, to
' keep the demonstration simple.
'
Dim assem As Assembly = GetType(Example).Assembly
' Get the type that is to be loaded, and create an instance
' of it. Activator.CreateInstance also has an overload that
' takes an array of types representing the types of the
' constructor parameters, if the type you are creating does
' not have a parameterless constructor. The new instance
' is stored as type Object, to maintain the fiction that
' nothing is known about the assembly. (Note that you can
' get the types in an assembly without knowing their names
' in advance.)
'
Dim tExForm As Type = assem.GetType("ExampleForm")
Dim exFormAsObj As Object = _
Activator.CreateInstance(tExForm)
' Get an EventInfo representing the Click event, and get the
' type of delegate that handles the event.
'
Dim evClick As EventInfo = tExForm.GetEvent("Click")
Dim tDelegate As Type = evClick.EventHandlerType
' If you already have a method with the correct signature,
' you can simply get a MethodInfo for it.
'
Dim miHandler As MethodInfo = _
GetType(Example).GetMethod("LuckyHandler", _
BindingFlags.NonPublic Or BindingFlags.Instance)
' Create an instance of the delegate. Using the overloads
' of CreateDelegate that take MethodInfo is recommended.
'
Dim d As [Delegate] = _
[Delegate].CreateDelegate(tDelegate, Me, miHandler)
' Get the "add" accessor of the event and invoke it late-
' bound, passing in the delegate instance. This is equivalent
' to using the += operator in C#, or AddHandler in Visual
' Basic. The instance on which the "add" accessor is invoked
' is the form; the arguments must be passed as an array.
'
Dim miAddHandler As MethodInfo = evClick.GetAddMethod()
Dim addHandlerArgs() As Object = {d}
miAddHandler.Invoke(exFormAsObj, addHandlerArgs)
' Event handler methods can also be generated at run time,
' using lightweight dynamic methods and Reflection.Emit.
' To construct an event handler, you need the return type
' and parameter types of the delegate. These can be obtained
' by examining the delegate's Invoke method.
'
' It is not necessary to name dynamic methods, so the empty
' string can be used. The last argument associates the
' dynamic method with the current type, giving the delegate
' access to all the public and private members of Example,
' as if it were an instance method.
'
Dim returnType As Type = GetDelegateReturnType(tDelegate)
If returnType IsNot GetType(Void) Then
Throw New ArgumentException("Delegate has a return type.", NameOf(d))
End If
Dim handler As New DynamicMethod( _
"", _
Nothing, _
GetDelegateParameterTypes(tDelegate), _
GetType(Example) _
)
' Generate a method body. This method loads a string, calls
' the Show method overload that takes a string, pops the
' return value off the stack (because the handler has no
' return type), and returns.
'
Dim ilgen As ILGenerator = handler.GetILGenerator()
Dim showParameters As Type() = {GetType(String)}
Dim simpleShow As MethodInfo = _
GetType(MessageBox).GetMethod("Show", showParameters)
ilgen.Emit(OpCodes.Ldstr, _
"This event handler was constructed at run time.")
ilgen.Emit(OpCodes.Call, simpleShow)
ilgen.Emit(OpCodes.Pop)
ilgen.Emit(OpCodes.Ret)
' Complete the dynamic method by calling its CreateDelegate
' method. Use the "add" accessor to add the delegate to
' the invocation list for the event.
'
Dim dEmitted As [Delegate] = handler.CreateDelegate(tDelegate)
miAddHandler.Invoke(exFormAsObj, New Object() {dEmitted})
' Show the form. Clicking on the form causes the two
' delegates to be invoked.
'
Application.Run(CType(exFormAsObj, Form))
End Sub
Private Sub LuckyHandler(ByVal sender As [Object], _
ByVal e As EventArgs)
MessageBox.Show("This event handler just happened to be lying around.")
End Sub
Private Function GetDelegateParameterTypes(ByVal d As Type) _
As Type()
If d.BaseType IsNot GetType(MulticastDelegate) Then
Throw New ArgumentException("Not a delegate.", NameOf(d))
End If
Dim invoke As MethodInfo = d.GetMethod("Invoke")
If invoke Is Nothing Then
Throw New ArgumentException("Not a delegate.", NameOf(d))
End If
Dim parameters As ParameterInfo() = invoke.GetParameters()
' Dimension this array Length - 1, because VB adds an extra
' element to zero-based arrays.
Dim typeParameters(parameters.Length - 1) As Type
For i As Integer = 0 To parameters.Length - 1
typeParameters(i) = parameters(i).ParameterType
Next i
Return typeParameters
End Function
Private Function GetDelegateReturnType(ByVal d As Type) As Type
If d.BaseType IsNot GetType(MulticastDelegate) Then
Throw New ArgumentException("Not a delegate.", NameOf(d))
End If
Dim invoke As MethodInfo = d.GetMethod("Invoke")
If invoke Is Nothing Then
Throw New ArgumentException("Not a delegate.", NameOf(d))
End If
Return invoke.ReturnType
End Function
End Class