Compartir a través de


Cómo: Enlazar un delegado mediante la reflexión

Cuando se usa la reflexión para cargar y ejecutar ensamblados, no se pueden usar características de lenguaje como el operador de C# += o la instrucción AddHandler de Visual Basic para enlazar eventos. Los procedimientos siguientes muestran cómo enlazar un método existente a un evento obteniendo todos los tipos necesarios a través de la reflexión y cómo crear un método dinámico mediante la emisión de reflexión y enlazarlo a un evento.

Nota:

Para obtener otra manera de enlazar un delegado de control de eventos, consulte el ejemplo de código para el método AddEventHandler de la clase EventInfo.

Para enlazar un delegado mediante la reflexión

  1. Cargue un ensamblado que contenga un tipo que genere eventos. Normalmente, los ensamblajes se cargan con el método Assembly.Load. Para simplificar este ejemplo, se usa un formulario derivado en el ensamblado actual, por lo que el GetExecutingAssembly método se usa para cargar el ensamblado actual.

    Assembly assem = typeof(Example).Assembly;
    
    Dim assem As Assembly = GetType(Example).Assembly
    
  2. Obtenga un Type objeto que represente el tipo y cree una instancia del tipo . El CreateInstance(Type) método se usa en el código siguiente porque el formulario tiene un constructor sin parámetros. Hay otras sobrecargas del método CreateInstance que puede usar si el tipo que está creando no tiene un constructor sin parámetro. La nueva instancia se almacena como tipo Object para mantener la ficción de que no se conoce nada sobre el ensamblado. La reflexión permite obtener los tipos de un ensamblaje sin conocer sus nombres de antemano.

    Type tExForm = assem.GetType("ExampleForm");
    Object exFormAsObj = Activator.CreateInstance(tExForm);
    
    Dim tExForm As Type = assem.GetType("ExampleForm")
    Dim exFormAsObj As Object = _
        Activator.CreateInstance(tExForm)
    
  3. Obtenga un EventInfo objeto que represente el evento y use la EventHandlerType propiedad para obtener el tipo de delegado usado para controlar el evento. En el siguiente código, se obtiene un EventInfo para el evento Click.

    EventInfo evClick = tExForm.GetEvent("Click");
    Type tDelegate = evClick.EventHandlerType;
    
    Dim evClick As EventInfo = tExForm.GetEvent("Click")
    Dim tDelegate As Type = evClick.EventHandlerType
    
  4. Obtiene un MethodInfo objeto que representa el método que controla el evento. El código de programa completo de la sección Ejemplo más adelante en este artículo contiene un método cuya firma coincide con la del delegado EventHandler, que controla el evento Click, pero también puede generar métodos dinámicos durante el tiempo de ejecución. Para obtener más información, consulte el procedimiento complementario para generar un controlador de eventos en tiempo de ejecución mediante un método dinámico.

    MethodInfo miHandler =
        typeof(Example).GetMethod("LuckyHandler",
            BindingFlags.NonPublic | BindingFlags.Instance);
    
    Dim miHandler As MethodInfo = _
        GetType(Example).GetMethod("LuckyHandler", _
            BindingFlags.NonPublic Or BindingFlags.Instance)
    
  5. Cree una instancia del delegado mediante el CreateDelegate método . Este método es estático (Shared en Visual Basic), por lo que se debe proporcionar el tipo de delegado. Se recomienda usar las sobrecargas de CreateDelegate que aceptan un MethodInfo.

    Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
    
    Dim d As [Delegate] = _
        [Delegate].CreateDelegate(tDelegate, Me, miHandler)
    
  6. Obtenga el método de acceso de add e invoquélo para enlazar el evento. Todos los eventos tienen un add método de acceso y un remove método de acceso, que están ocultos por la sintaxis de los lenguajes de alto nivel. Por ejemplo, C# usa el += operador para enlazar eventos y Visual Basic usa la instrucción AddHandler. El código siguiente obtiene el add descriptor de acceso del Click evento y lo invoca dinámicamente, pasando la instancia del delegado. Los argumentos se deben pasar como una matriz.

    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)
    
  7. Pruebe el evento. El código siguiente muestra el formulario definido en el ejemplo de código. Al hacer clic en el formulario, se invoca al controlador de eventos.

    Application.Run((Form) exFormAsObj);
    
    Application.Run(CType(exFormAsObj, Form))
    

Generación de un controlador de eventos en tiempo de ejecución mediante un método dinámico

  1. Los métodos del controlador de eventos se pueden generar en tiempo de ejecución mediante métodos dinámicos ligeros y emisión de reflexión. Para construir un controlador de eventos, necesita conocer el tipo de retorno y los tipos de parámetros del delegado. Se pueden obtener examinando el método del Invoke delegado. El código siguiente usa los GetDelegateReturnType métodos y GetDelegateParameterTypes para obtener esta información. El código de estos métodos se puede encontrar en la sección Ejemplo más adelante en este artículo.

    No es necesario asignar un nombre a , DynamicMethodpor lo que se puede usar la cadena vacía. En el código siguiente, el último argumento asocia el método dinámico con el tipo actual, lo que proporciona al delegado acceso a todos los miembros públicos y privados de la Example clase.

    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) _
    )
    
  2. Genere un cuerpo del método. Este método carga una cadena, llama a la MessageBox.Show sobrecarga del método que toma una cadena, quita el valor devuelto de la pila (porque el controlador no tiene ningún tipo de valor devuelto) y devuelve. Para obtener más información sobre cómo emitir métodos dinámicos, vea How to: Define and Execute Dynamic Methods.

    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)
    
  3. Complete el método dinámico llamando a el método CreateDelegate. Usa el add accesor para agregar el delegado a la lista de invocación del evento.

    Delegate dEmitted = handler.CreateDelegate(tDelegate);
    addHandler.Invoke(exFormAsObj, new Object[] { dEmitted });
    
    Dim dEmitted As [Delegate] = handler.CreateDelegate(tDelegate)
    miAddHandler.Invoke(exFormAsObj, New Object() {dEmitted})
    
  4. Pruebe el evento. El código siguiente carga el formulario definido en el ejemplo de código. Al hacer clic en el formulario, se invoca el controlador de eventos predefinido y el controlador de eventos emitido.

    Application.Run((Form) exFormAsObj);
    
    Application.Run(CType(exFormAsObj, Form))
    

Ejemplo

En el ejemplo de código siguiente se muestra cómo enlazar un método existente a un evento mediante la reflexión y cómo usar la DynamicMethod clase para emitir un método en tiempo de ejecución y enlazarlo a un evento.

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

Consulte también