Gewusst wie: Verknüpfen mit einem Delegaten anhand von Reflektion
Markieren Sie das Kontrollkästchen Englisch, um die englische Version dieses Artikels anzuzeigen. Sie können den englischen Text auch in einem Popup-Fenster einblenden, indem Sie den Mauszeiger über den Text bewegen.
Übersetzung
Englisch

Gewusst wie: Verknüpfen mit einem Delegaten anhand von Reflektion

 

Wenn Sie Assemblys mithilfe von Reflektion laden und ausführen, können Sie Ereignisse nicht mit Sprachfeatures wie dem Operator += von C# oder der AddHandler-Anweisung von Visual Basic verknüpfen. In den folgenden Verfahrensweisen wird veranschaulicht, wie eine vorhandene Methode mit einem Ereignis verknüpft wird, indem alle erforderlichen Typen über Reflektion abgerufen werden, und wie eine dynamische Methode mithilfe von Reflektionsausgabe erstellt und mit einem Ereignis verknüpft wird.

System_CAPS_noteHinweis

Eine andere Möglichkeit, einen Delegaten für die Ereignisbehandlung zu verknüpfen, zeigt das Codebeispiel für die AddEventHandler-Methode der EventInfo-Klasse.

So verknüpfen Sie einen Delegaten mithilfe von Reflektion

  1. Laden Sie eine Assembly, die einen Typ enthält, der Ereignisse auslöst. Assemblys werden üblicherweise anhand der Assembly.Load-Methode geladen. Um dieses Beispiel möglichst einfach zu halten, wird ein abgeleitetes Formular in der aktuellen Assembly verwendet, es wird daher die GetExecutingAssembly-Methode zum Laden der aktuellen Assembly verwendet.

    Assembly assem = typeof(Example).Assembly;
    
  2. Rufen Sie ein Type-Objekt ab, das den Typ darstellt, und erstellen Sie eine Instanz des Typs. Da das Formular über einen Standardkonstruktor verfügt, wird im folgenden Code die CreateInstance(Type)-Methode verwendet. Es gibt verschiedene andere Überladungen der CreateInstance-Methode, die Sie verwenden können, wenn der erstellte Typ nicht über einen Standardkonstruktor verfügt. Die neue Instanz wird als Typ Object gespeichert, um bei der Idee zu bleiben, dass keine Informationen über die Assembly bekannt sind. (Mithilfe der Reflektion können Sie die Typen in einer Assembly abrufen, ohne ihre Namen bereits zu kennen.)

    Type tExForm = assem.GetType("ExampleForm");
    Object exFormAsObj = Activator.CreateInstance(tExForm);
    
  3. Rufen Sie ein EventInfo-Objekt ab, dass das Ereignis darstellt, und rufen Sie mithilfe der EventHandlerType-Eigenschaft den Typ des Delegaten ab, der für die Ereignisbehandlung verwendet wird. Im folgenden Code wird ein EventInfo für das Click-Ereignis abgerufen.

    EventInfo evClick = tExForm.GetEvent("Click");
    Type tDelegate = evClick.EventHandlerType;
    
  4. Rufen Sie ein MethodInfo-Objekt ab, das die Methode darstellt, die das Ereignis behandelt. Der vollständige Programmcode im Thema "Beispiel" weiter unten enthält eine Methode, die der Signatur des EventHandler-Delegaten entspricht, der das Click-Ereignis behandelt, zur Laufzeit können Sie auch dynamische Methoden generieren. Einzelheiten finden Sie in der zugehörigen Prozedur zum Generieren eines Ereignishandlers zur Laufzeit durch eine dynamische Methode.

    MethodInfo miHandler = 
        typeof(Example).GetMethod("LuckyHandler", 
            BindingFlags.NonPublic | BindingFlags.Instance);
    
  5. Erstellen Sie mithilfe der CreateDelegate-Methode eine Instanz des Delegaten. Diese Methode ist statisch (Shared in Visual Basic), daher muss der Delegattyp angegeben werden. Es empfiehlt sich, die Überladungen von CreateDelegate zu verwenden, die eine MethodInfo akzeptieren.

    Delegate d = Delegate.CreateDelegate(tDelegate, this, miHandler);
    
  6. Rufen Sie die add-Accessormethode ab, und rufen Sie sie auf, auf das Ereignis zu verknüpfen. Alle Ereignisse verfügen über einen add-Accessor und einen remove-Accessor, die durch die Syntax hoch entwickelter Sprachen verborgen sind. Ereignisse werden beispielsweise in C# mit dem Operator += und in Visual Basic mit der AddHandler-Anweisung verknüpft. Im folgenden Code wird der add-Accessor des Click-Ereignisses abgerufen und dieses dann spät gebunden aufgerufen, indem die Delegatinstanz übergeben wird. Die Argumente müssen als Array übergeben werden.

    MethodInfo addHandler = evClick.GetAddMethod();
    Object[] addHandlerArgs = { d };
    addHandler.Invoke(exFormAsObj, addHandlerArgs);
    
  7. Testen Sie das Ereignis. Im folgenden Code wird das im Codebeispiel definierte Formular veranschaulicht. Wenn Sie auf das Formular klicken, wird der Ereignishandler aufgerufen.

    Application.Run((Form) exFormAsObj);
    

So generieren Sie anhand einer dynamischen Methode zur Laufzeit einen Ereignishandler

  1. Ereignishandlermethoden können mithilfe einfacher dynamischer Methoden und Reflektionsausgabe zur Laufzeit generiert werden. Zum Erstellen eines Ereignishandlers benötigen Sie den Rückgabetyp und die Parametertypen des Delegaten. Diese können über die Invoke-Methode des Delegaten abgerufen werden. Im folgenden Code werden diese Informationen über die GetDelegateReturnType-Methode und die GetDelegateParameterTypes-Methode ermittelt. Den Code für diese Methoden können Sie später in diesem Thema im Abschnitt Beispiel finden.

    Eine DynamicMethod muss nicht bezeichnet werden, Sie können eine leere Zeichenfolge verwenden. Im folgenden Code wird die dynamische Methode mit dem letzten Argument dem aktuellen Typ zugeordnet. Dadurch erhält der Delegat Zugriff auf alle öffentlichen und privaten Member der Example-Klasse.

    Type returnType = GetDelegateReturnType(tDelegate);
    if (returnType != typeof(void))
        throw new ApplicationException("Delegate has a return type.");
    
    DynamicMethod handler = 
        new DynamicMethod("", 
                          null,
                          GetDelegateParameterTypes(tDelegate),
                          typeof(Example));
    
  2. Generieren Sie einen Methodentext. Diese Methode lädt eine Zeichenfolge, ruft die Überladung der MessageBox.Show-Methode auf, die eine Zeichenfolge akzeptiert, nimmt den Rückgabewert vom Stapel auf (da der Handler keinen Rückgabetyp aufweist) und wird dann beendet. Weitere Informationen zum Ausgeben dynamischer Methoden finden Sie unter Gewusst wie: Definieren und Ausführen von dynamischen Methoden.

    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);
    
  3. Schließen Sie die dynamische Methode ab, indem Sie seine CreateDelegate-Methode aufrufen. Fügen Sie den Delegaten mithilfe des add-Accessors zur Aufrufliste des Ereignisses hinzu.

    Delegate dEmitted = handler.CreateDelegate(tDelegate);
    addHandler.Invoke(exFormAsObj, new Object[] { dEmitted });
    
  4. Testen Sie das Ereignis. Im folgenden Code wird das im Codebeispiel definierte Formular geladen. Wenn Sie auf das Formular klicken, werden sowohl der vordefinierte als auch der ausgegebene Ereignishandler aufgerufen.

    Application.Run((Form) exFormAsObj);
    

Beispiel

In den folgenden Codebeispielen wird veranschaulicht, wie eine vorhandene Methode anhand von Reflektion mit einem Ereignis verknüpft wird, und wie eine Methode mithilfe der DynamicMethod-Klasse zur Laufzeit ausgegeben und mit einem Ereignis verknüpft wird.

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 ApplicationException("Delegate has a return type.");

        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 ApplicationException("Not a delegate.");

        MethodInfo invoke = d.GetMethod("Invoke");
        if (invoke == null)
            throw new ApplicationException("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 Type GetDelegateReturnType(Type d)
    {
        if (d.BaseType != typeof(MulticastDelegate))
            throw new ApplicationException("Not a delegate.");

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

        return invoke.ReturnType;
    }
}

Kompilieren des Codes

  • Der Code enthält die für die Kompilierung erforderlichen using-Anweisungen für C# (Imports in Visual Basic).

  • Für die Kompilierung von der Befehlszeile aus sind keine zusätzlichen Assemblyverweise erforderlich. In Visual Studio müssen Sie einen Verweis auf System.Windows.Forms.dll hinzufügen, da es sich bei diesem Beispiel um eine Konsolenanwendung handelt.

  • Kompilieren Sie den Code über die Befehlszeile mit csc.exe, vbc.exe oder cl.exe. Um den Code in Visual Studio zu kompilieren, fügen Sie ihn in eine Projektvorlage für eine Konsolenanwendung ein.

Anzeigen:
© 2016 Microsoft