Partager via


How to: Hook Up a Delegate Using Reflection

Microsoft Silverlight will reach end of support after October 2021. Learn more.

When you use reflection to load and run assemblies, you cannot use language features like the C# += operator or the Visual Basic AddHandler statement to hook up events. The following procedures show how to hook up an existing method to an event, and how to create an anonymously hosted dynamic method using reflection emit and hook it up to an event. (In Silverlight, all dynamic methods are anonymously hosted.) All the necessary information is obtained through reflection.

NoteNote:

For another example of hooking up an event-handling delegate, see the code example for the AddEventHandler method of the EventInfo class.

To hook up a delegate using reflection

  1. Load an assembly that contains a type that raises events. Assemblies are usually loaded with the Assembly.Load method. To keep this example simple, an object in a currently loaded assembly is used.

  2. Get an unknown object, or create an object of a type found in an assembly that you have loaded. To keep the example simple, the object that is used is the TextBlock that is provided for the example's output; however, the TextBlock is stored as type Object to maintain the fiction that nothing is known about the object or the assembly that it comes from.

    Dim obj As Object = outputBlock
    
    object obj = outputBlock;
    
  3. Get an EventInfo object that represents the event, and use the EventHandlerType property to get the type of delegate used to handle the event. In the following code, an EventInfo for the MouseLeftButtonUp event is obtained. This example looks for the event by name, but the Type.GetEvents method could be used to see which events exist.

    Dim evMouseUp As EventInfo = _
       outputBlock.GetType().GetEvent("MouseLeftButtonUp")
    Dim tDelegate As Type = evMouseUp.EventHandlerType
    
    EventInfo evMouseUp = outputBlock.GetType().GetEvent("MouseLeftButtonUp");
    Type tDelegate = evMouseUp.EventHandlerType;
    
  4. Get a MethodInfo object that represents the method that handles the event. The complete program code in the Example section later in this topic contains a method named LuckyHandler that is compatible with the signature of the delegate that handles the MouseLeftButtonUp event. (The accompanying procedure To Generate an Event Handler at Run Time by Using a Dynamic Method shows how to generate dynamic event handlers at run time.)

    NoteNote:

    A method is compatible with a delegate if the delegate's parameter types are subclasses of the method's parameters, and the method's return type is a subclass of the delegate's return type. In this case, the parameters of the delegate type, MouseButtonEventHandler, are subclasses of the parameters of the method LuckyHandler. See Delegate.

    Dim miHandler As MethodInfo = _
        GetType(Example).GetMethod("LuckyHandler", _
            BindingFlags.NonPublic Or BindingFlags.Static)  
    
    MethodInfo miHandler = 
       typeof(Example).GetMethod("LuckyHandler", 
                     BindingFlags.NonPublic | BindingFlags.Static);
    
  5. Create an instance of the delegate, using the CreateDelegate method. This method is static (Shared in Visual Basic), so the delegate type must be supplied as a parameter. We recommend that you use the overloads of CreateDelegate that take a MethodInfo.

    Dim d As [Delegate] = _
        [Delegate].CreateDelegate(tDelegate, miHandler) 
    
    Delegate d = Delegate.CreateDelegate(tDelegate, miHandler);
    
  6. Get the add accessor method and invoke it to hook up the event. All events have an add accessor and a remove accessor, which are hidden by the syntax of high-level languages. For example, C# uses the += operator to hook up events, and Visual Basic uses the AddHandler statement. The following code gets the add accessor of the MouseLeftButtonUp event and invokes it late-bound, passing in the delegate instance. The arguments must be passed as an array.

    NoteNote:

    A slightly simpler technique is to use the EventInfo.AddEventHandler handler method, as shown in the accompanying procedure To Generate an Event Handler at Run Time by Using a Dynamic Method.

    Dim miAddHandler As MethodInfo = evMouseUp.GetAddMethod()
    Dim addHandlerArgs() As Object = { d }
    miAddHandler.Invoke(obj, addHandlerArgs)
    
    MethodInfo miAddHandler = evMouseUp.GetAddMethod();
    object[] addHandlerArgs = { d };
    miAddHandler.Invoke(obj, addHandlerArgs);
    
  7. Test the event. The following code displays a message on the TextBlock, inviting you to demonstrate the event handler by clicking.

    outputBlock.Text &= "Click here to invoke the two delegates." & vbLf
    
    outputBlock.Text += "Click here to invoke the two delegates.\n";
    

To generate an event handler at run time by using a dynamic method

  1. Event-handler methods can 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. The following code uses the GetDelegateReturnType and GetDelegateParameterTypes methods to obtain this information by using reflection. The code for these methods can be found in the Example section later in this topic.

    It is not necessary to name a DynamicMethod, so the empty string can be used.

    Dim returnType As Type = GetDelegateReturnType(tDelegate)
    If returnType IsNot GetType(Void) Then
        Throw New InvalidOperationException("Delegate has a return type.")
    End If
    
    Dim handler As _
        New DynamicMethod("", Nothing, GetDelegateParameterTypes(tDelegate))
    
    Type returnType = GetDelegateReturnType(tDelegate);
    if (returnType != typeof(void))
    {
        throw new InvalidOperationException("Delegate has a return type.");
    }
    
    DynamicMethod handler = 
       new DynamicMethod("", null, GetDelegateParameterTypes(tDelegate));
    
  2. Generate a method body. This method loads a string, calls the overload of the Show method that takes a single string, pops the return value off the stack (because the handler has no return type), and returns. To learn more about emitting dynamic methods, see How to: Define and Execute Dynamic Methods.

    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)
    
    ILGenerator ilgen = handler.GetILGenerator();
    
    Type[] showParameters = { typeof(string) };
    MethodInfo simpleShow = 
       typeof(System.Windows.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);
    
  3. 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)
    evMouseUp.AddEventHandler(obj, dEmitted)
    
    Delegate dEmitted = handler.CreateDelegate(tDelegate);
    evMouseUp.AddEventHandler(obj, dEmitted); 
    
  4. Test the event. The following code displays a message on the TextBlock, inviting you to demonstrate the event handler by clicking.

    outputBlock.Text &= "Click here to invoke the two delegates." & vbLf
    
    outputBlock.Text += "Click here to invoke the two delegates.\n";
    

Example

The following code example shows how to hook up an existing method to an event by using reflection, and also how to use the DynamicMethod class to emit a method at run time and hook it up to an event.

Imports System.Reflection
Imports System.Reflection.Emit

Class Example

    Public Shared Sub Demo(ByVal outputBlock As System.Windows.Controls.TextBlock) 

        ' Get the type whose event is to be handled. This example
        ' uses the TextBlock that is provided. 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 obj As Object = outputBlock

        ' Get an EventInfo that represents the MouseLeftButtonUp 
        ' event, and get the type of delegate that handles the event.
        '
        Dim evMouseUp As EventInfo = _
           outputBlock.GetType().GetEvent("MouseLeftButtonUp")
        Dim tDelegate As Type = evMouseUp.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.Static)  
        ' Create an instance of the delegate. Using the overloads
        ' of CreateDelegate that take MethodInfo is recommended.
        '
        Dim d As [Delegate] = _
            [Delegate].CreateDelegate(tDelegate, 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.
        ' Note that you can also use EventInfo.AddEventHandler, and 
        ' avoid making a late-bound call.
        '
        Dim miAddHandler As MethodInfo = evMouseUp.GetAddMethod()
        Dim addHandlerArgs() As Object = { d }
        miAddHandler.Invoke(obj, 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. 
        '
        Dim returnType As Type = GetDelegateReturnType(tDelegate)
        If returnType IsNot GetType(Void) Then
            Throw New InvalidOperationException("Delegate has a return type.")
        End If

        Dim handler As _
            New DynamicMethod("", Nothing, GetDelegateParameterTypes(tDelegate))

        ' 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)
        evMouseUp.AddEventHandler(obj, dEmitted)

        ' Clicking on the TextBlock now causes the two delegates to
        ' be invoked.
        '
        outputBlock.Text &= "Click here to invoke the two delegates." & vbLf

    End Sub

    Private Shared Sub LuckyHandler(ByVal sender As Object, _
        ByVal e As EventArgs) 

        MessageBox.Show("This event handler just happened to be lying around.")
    End Sub

    Private Shared Function GetDelegateParameterTypes(ByVal d As Type) _
        As Type() 

        If d.BaseType IsNot GetType(MulticastDelegate) Then
            Throw New InvalidOperationException("Not a delegate.")
        End If

        Dim invoke As MethodInfo = d.GetMethod("Invoke")
        If invoke Is Nothing Then
            Throw New InvalidOperationException("Not a delegate.")
        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 Shared Function GetDelegateReturnType(ByVal d As Type) As Type 

        If d.BaseType IsNot GetType(MulticastDelegate) Then
            Throw New InvalidOperationException("Not a delegate.")
        End If

        Dim invoke As MethodInfo = d.GetMethod("Invoke")
        If invoke Is Nothing Then
            Throw New InvalidOperationException("Not a delegate.")
        End If

        Return invoke.ReturnType

    End Function 
End Class 
using System;
using System.Reflection;
using System.Reflection.Emit;

class Example
{
    public static void Demo(System.Windows.Controls.TextBlock outputBlock)
    {
        // Get the type whose event is to be handled. This example
        // uses the TextBlock that is provided. 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.)
        //
        object obj = outputBlock;

        // Get an EventInfo that represents the MouseLeftButtonUp 
        // event, and get the type of delegate that handles the event.
        //
        EventInfo evMouseUp = outputBlock.GetType().GetEvent("MouseLeftButtonUp");
        Type tDelegate = evMouseUp.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.Static);
        // Create an instance of the delegate. Using the overloads
        // of CreateDelegate that take MethodInfo is recommended.
        //
        Delegate d = Delegate.CreateDelegate(tDelegate, 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.
        // Note that you can also use EventInfo.AddEventHandler, and 
        // avoid making a late-bound call.
        //
        MethodInfo miAddHandler = evMouseUp.GetAddMethod();
        object[] addHandlerArgs = { d };
        miAddHandler.Invoke(obj, 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. 
        //
        Type returnType = GetDelegateReturnType(tDelegate);
        if (returnType != typeof(void))
        {
            throw new InvalidOperationException("Delegate has a return type.");
        }

        DynamicMethod handler = 
           new DynamicMethod("", null, GetDelegateParameterTypes(tDelegate));

        // 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(System.Windows.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 EventInfo.AddEventHandler method to add the 
        // delegate to the invocation list for the event. Alternatively
        // you could use the "add" accessor, as shown earlier in this
        // example.
        Delegate dEmitted = handler.CreateDelegate(tDelegate);
        evMouseUp.AddEventHandler(obj, dEmitted); 

        // Clicking on the TextBlock now causes the two delegates to
        // be invoked.
        //
        outputBlock.Text += "Click here to invoke the two delegates.\n";
    }

    private static void LuckyHandler(object sender, EventArgs e)
    {
        System.Windows.MessageBox.Show(
           "This event handler just happened to be lying around.");
    }

    private static Type[] GetDelegateParameterTypes(Type d)
    {
        if (d.BaseType != typeof(MulticastDelegate))
        {
            throw new InvalidOperationException("Not a delegate.");
        }

        MethodInfo invoke = d.GetMethod("Invoke");
        if (invoke == null)
        {
            throw new InvalidOperationException("Not a delegate.");
        }

        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 static Type GetDelegateReturnType(Type d)
    {
        if (d.BaseType != typeof(MulticastDelegate))
        {
            throw new InvalidOperationException("Not a delegate.");
        }

        MethodInfo invoke = d.GetMethod("Invoke");
        if (invoke == null)
        {
            throw new InvalidOperationException("Not a delegate.");
        }

        return invoke.ReturnType;
    }
}

Compiling the Code

  • The code contains the C# using statements (Imports in Visual Basic) that are necessary for compilation.

  • The code contains a static (Shared in Visual Basic) Demo method that has one parameter, a TextBlock that is used to display the output. For instructions on building the code as part of a simple Silverlight-based application, see Building Examples That Use a Demo Method and a TextBlock Control. You can put all the code in this example into one source file.