Gewusst wie: Definieren und Ausführen von dynamischen Methoden

In den folgenden Verfahren wird beschrieben, wie eine einfache dynamische Methode und eine dynamische Methode, die an eine Instanz einer Klasse gebunden ist, definiert und ausgeführt werden. Weitere Informationen über dynamische Methoden finden Sie unter der DynamicMethod-Klasse und unter Szenarien für die Reflektionsausgabe mit dynamischen Methoden.

So definieren Sie eine dynamische Methode und führen diese aus

  1. Deklarieren Sie einen Delegattyp, um die Methode auszuführen. Es empfiehlt sich, einen generischen Delegaten zu verwenden, um die Anzahl der zu deklarierenden Delegattypen möglichst gering zu halten. Im folgenden Code werden zwei Delegattypen (einer davon generisch) deklariert, die für die SquareIt-Methode verwendet werden können.

    
    private delegate long SquareItInvoker(int input);
    
    private delegate TReturn OneParameter<TReturn, TParameter0>
        (TParameter0 p0);
    
    
    
  2. Erstellen Sie ein Array, das die Parametertypen für die dynamische Methode angibt. Der einzige Parameter in diesem Beispiel ist ein int (Integer in Visual Basic), sodass das Array nur ein Element enthält.

    
    Type[] methodArgs = {typeof(int)};
    
    
    
  3. Erstellen Sie eine DynamicMethod. In diesem Beispiel trägt die Methode die Bezeichnung SquareIt.

    Hinweis Hinweis

    Dynamische Methoden müssen nicht bezeichnet werden, denn sie können nicht anhand des Namens aufgerufen werden. Mehrere dynamische Methoden können den gleichen Namen haben. Der Name wird jedoch in Aufruflisten angezeigt und kann für Debuggen nützlich sein.

    Der Typ des Rückgabewerts wird als long angegeben. Die Methode ist dem Modul zugeordnet, das die Example-Klasse mit dem Beispielcode enthält. Es kann jedes geladene Modul angegeben werden. Die dynamischen Methoden funktionieren wie eine static-Methode (Shared in Visual Basic) auf Modulebene.

    
    DynamicMethod squareIt = new DynamicMethod(
        "SquareIt", 
        typeof(long), 
        methodArgs, 
        typeof(Example).Module);
    
    
    
  4. Geben Sie den Methodentext aus. In diesem Beispiel wird ein ILGenerator-Objekt verwendet, um die Microsoft Intermediate Language (MSIL) auszugeben. Der Methodentext einer DynamicMethod kann jedoch auch über ein DynamicILInfo-Objekt in Verbindung mit nicht verwalteten Code-Generatoren ausgegeben werden.

    Der MSIL-Code in diesem Beispiel lädt das Argument, bei dem es sich um ein int handelt, auf den Stapel, konvertiert es in ein long, dupliziert das long und multipliziert die beiden Zahlen. Somit verbleibt das quadrierte Ergebnis auf dem Stapel, und die Methode kann beendet werden.

    
    ILGenerator il = squareIt.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Conv_I8);
    il.Emit(OpCodes.Dup);
    il.Emit(OpCodes.Mul);
    il.Emit(OpCodes.Ret);
    
    
    
  5. Erstellen Sie eine Instanz des in Schritt 1 deklarierten Delegaten, der die dynamische Methode durch Aufrufen der CreateDelegate-Methode darstellt. Mit der Erstellung des Delegaten ist die Methode abgeschlossen. Alle Versuche, die Methode zu ändern, z. B. durch Hinzufügen von weiterem MSIL-Code, werden ignoriert. Im folgenden Code wird der Delegat erstellt und mithilfe eines generischen Delegaten aufgerufen.

    
    OneParameter<long, int> invokeSquareIt = 
        (OneParameter<long, int>)
        squareIt.CreateDelegate(typeof(OneParameter<long, int>));
    
    Console.WriteLine("123456789 squared = {0}",
        invokeSquareIt(123456789));
    
    
    

So definieren Sie eine dynamische Methode, die an ein Objekt gebunden ist, und führen diese aus

  1. Deklarieren Sie einen Delegattyp, um die Methode auszuführen. Es empfiehlt sich, einen generischen Delegaten zu verwenden, um die Anzahl der zu deklarierenden Delegattypen möglichst gering zu halten. Im folgenden Code wird ein generischer Delegattyp deklariert, mit dem alle Methoden, die über einen Parameter und einen Rückgabewert oder, sofern der Delegat an ein Objekt gebunden ist, über zwei Parameter und einen Rückgabewert verfügen, ausgeführt werden können.

    
    private delegate TReturn OneParameter<TReturn, TParameter0>
        (TParameter0 p0);
    
    
    
  2. Erstellen Sie ein Array, das die Parametertypen für die dynamische Methode angibt. Wenn der Delegat, der die Methode darstellt, an ein Objekt gebunden werden soll, muss der erste Parameter dem Typ entsprechen, an den der Delegat gebunden wird. In diesem Beispiel gibt es zwei Parameter, vom Typ Example und Typ int (Integer in Visual Basic).

    
    Type[] methodArgs2 = { typeof(Example), typeof(int) };
    
    
    
  3. Erstellen Sie eine DynamicMethod. In diesem Beispiel verfügt die Methode über keinen Namen. Der Typ des Rückgabewerts wird als int (Integer in Visual Basic) angegeben. Die Methode kann auf private und geschützte Member der Example-Klasse zugreifen.

    
    DynamicMethod multiplyHidden = new DynamicMethod(
        "", 
        typeof(int), 
        methodArgs2, 
        typeof(Example));
    
    
    
  4. Geben Sie den Methodentext aus. In diesem Beispiel wird ein ILGenerator-Objekt verwendet, um die Microsoft Intermediate Language (MSIL) auszugeben. Der Methodentext einer DynamicMethod kann jedoch auch über ein DynamicILInfo-Objekt in Verbindung mit nicht verwalteten Code-Generatoren ausgegeben werden.

    Der MSIL-Code in diesem Beispiel lädt das erste Argument, bei dem es sich um eine Instanz der Example-Klasse handelt, und lädt mit dieser den Wert eines privaten Instanzfeldes vom Typ int. Das zweite Argument wird geladen, und die beiden Zahlen werden multipliziert. Wenn das Ergebnis größer als int ist, wird der Wert abgeschnitten, und die wichtigsten Bits werden verworfen. Die Methode wird beendet, wobei der Rückgabewert auf dem Stapel abgelegt wird.

    
    ILGenerator ilMH = multiplyHidden.GetILGenerator();
    ilMH.Emit(OpCodes.Ldarg_0);
    
    FieldInfo testInfo = typeof(Example).GetField("test",
        BindingFlags.NonPublic | BindingFlags.Instance);
    
    ilMH.Emit(OpCodes.Ldfld, testInfo);
    ilMH.Emit(OpCodes.Ldarg_1);
    ilMH.Emit(OpCodes.Mul);
    ilMH.Emit(OpCodes.Ret);
    
    
    
  5. Erstellen Sie eine Instanz des in Schritt 1 deklarierten Delegaten, der die dynamische Methode durch Aufrufen der CreateDelegate(Type, Object)-Methodenüberladung darstellt. Mit der Erstellung des Delegaten ist die Methode abgeschlossen. Alle Versuche, die Methode zu ändern, z. B. durch Hinzufügen von weiterem MSIL-Code, werden ignoriert.

    Hinweis Hinweis

    Die CreateDelegate-Methode kann mehrfach aufgerufen werden, um Delegaten zu erstellen, die an andere Instanzen des Zieltyps gebunden sind.

    Im folgenden Code wird die Methode an eine neue Instanz der Example-Klasse gebunden, deren privates Testfeld auf 42 festgelegt ist. Auf diese Weise wird bei jedem Aufruf des Delegaten die Instanz von Example an den ersten Parameter der Methode übergeben.

    Der Delegat OneParameter wird verwendet, da der erste Parameter der Methode immer die Instanz von Example erhält. Wenn der Delegat aufgerufen wird, ist nur der zweite Parameter erforderlich.

    
    OneParameter<int, int> invoke = (OneParameter<int, int>)
        multiplyHidden.CreateDelegate(
            typeof(OneParameter<int, int>), 
            new Example(42)
        );
    
    Console.WriteLine("3 * test = {0}", invoke(3));
    
    
    

Im folgenden Codebeispiel werden eine einfache dynamische Methode und eine dynamische Methode, die an eine Instanz einer Klasse gebunden ist, veranschaulicht.

Die einfache dynamische Methode nimmt ein Argument an, einen 32-Bit-Ganzzahl, und gibt die das 64-Bit-Quadrat dieser Ganzzahl zurück. Die Methode wird mithilfe eines generischen Delegaten aufgerufen.

Die zweite dynamische Methode verfügt über zwei Parameter, vom Typ Example und vom Typ int (Integer in Visual Basic). Nach der Erstellung wird die dynamische Methode an eine Instanz von Example gebunden. Dafür wird ein generischer Delegat mit einem Argument vom Typ int verwendet. Der Delegat verfügt über kein Argument vom Typ Example, da der erste Parameter der Methode immer die gebundene Instanz von Example erhält. Wenn der Delegat aufgerufen wird, wird nur das int-Argument angegeben. Diese dynamische Methode greift auf ein privates Feld der Example-Klasse zu und gibt das Produkt des privaten Feldes und des int-Arguments zurück.

Im Codebeispiel werden Delegaten definiert, die zum Ausführen der Methoden verwendet werden können.


using System;
using System.Reflection;
using System.Reflection.Emit;

public class Example
{
    // The following constructor and private field are used to
    // demonstrate a method bound to an object.
    private int test;
    public Example(int test) { this.test = test; }

    // Declare delegates that can be used to execute the completed 
    // SquareIt dynamic method. The OneParameter delegate can be 
    // used to execute any method with one parameter and a return
    // value, or a method with two parameters and a return value
    // if the delegate is bound to an object.
    //
    private delegate long SquareItInvoker(int input);

    private delegate TReturn OneParameter<TReturn, TParameter0>
        (TParameter0 p0);

    public static void Main()
    {
        // Example 1: A simple dynamic method.
        //
        // Create an array that specifies the parameter types for the
        // dynamic method. In this example the only parameter is an 
        // int, so the array has only one element.
        //
        Type[] methodArgs = {typeof(int)};

        // Create a DynamicMethod. In this example the method is
        // named SquareIt. It is not necessary to give dynamic 
        // methods names. They cannot be invoked by name, and two
        // dynamic methods can have the same name. However, the 
        // name appears in calls stacks and can be useful for
        // debugging. 
        //
        // In this example the return type of the dynamic method
        // is long. The method is associated with the module that 
        // contains the Example class. Any loaded module could be
        // specified. The dynamic method is like a module-level
        // static method.
        //
        DynamicMethod squareIt = new DynamicMethod(
            "SquareIt", 
            typeof(long), 
            methodArgs, 
            typeof(Example).Module);

        // Emit the method body. In this example ILGenerator is used
        // to emit the MSIL. DynamicMethod has an associated type
        // DynamicILInfo that can be used in conjunction with 
        // unmanaged code generators.
        //
        // The MSIL loads the argument, which is an int, onto the 
        // stack, converts the int to a long, duplicates the top
        // item on the stack, and multiplies the top two items on the
        // stack. This leaves the squared number on the stack, and 
        // all the method has to do is return.
        //
        ILGenerator il = squareIt.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Conv_I8);
        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Mul);
        il.Emit(OpCodes.Ret);

        // Create a delegate that represents the dynamic method. 
        // Creating the delegate completes the method, and any further 
        // attempts to change the method (for example, by adding more
        // MSIL) are ignored. The following code uses a generic 
        // delegate that can produce delegate types matching any
        // single-parameter method that has a return type.
        //
        OneParameter<long, int> invokeSquareIt = 
            (OneParameter<long, int>)
            squareIt.CreateDelegate(typeof(OneParameter<long, int>));

        Console.WriteLine("123456789 squared = {0}",
            invokeSquareIt(123456789));

        // Example 2: A dynamic method bound to an instance.
        //
        // Create an array that specifies the parameter types for a
        // dynamic method. If the delegate representing the method
        // is to be bound to an object, the first parameter must 
        // match the type the delegate is bound to. In the following
        // code the bound instance is of the Example class. 
        //
        Type[] methodArgs2 = { typeof(Example), typeof(int) };

        // Create a DynamicMethod. In this example the method has no
        // name. The return type of the method is int. The method 
        // has access to the protected and private data of the 
        // Example class.
        //
        DynamicMethod multiplyHidden = new DynamicMethod(
            "", 
            typeof(int), 
            methodArgs2, 
            typeof(Example));

        // Emit the method body. In this example ILGenerator is used
        // to emit the MSIL. DynamicMethod has an associated type
        // DynamicILInfo that can be used in conjunction with 
        // unmanaged code generators.
        //
        // The MSIL loads the first argument, which is an instance of
        // the Example class, and uses it to load the value of a 
        // private instance field of type int. The second argument is
        // loaded, and the two numbers are multiplied. If the result
        // is larger than int, the value is truncated and the most 
        // significant bits are discarded. The method returns, with
        // the return value on the stack.
        //
        ILGenerator ilMH = multiplyHidden.GetILGenerator();
        ilMH.Emit(OpCodes.Ldarg_0);

        FieldInfo testInfo = typeof(Example).GetField("test",
            BindingFlags.NonPublic | BindingFlags.Instance);

        ilMH.Emit(OpCodes.Ldfld, testInfo);
        ilMH.Emit(OpCodes.Ldarg_1);
        ilMH.Emit(OpCodes.Mul);
        ilMH.Emit(OpCodes.Ret);

        // Create a delegate that represents the dynamic method. 
        // Creating the delegate completes the method, and any further 
        // attempts to change the method � for example, by adding more
        // MSIL � are ignored. 
        // 
        // The following code binds the method to a new instance
        // of the Example class whose private test field is set to 42.
        // That is, each time the delegate is invoked the instance of
        // Example is passed to the first parameter of the method.
        //
        // The delegate OneParameter is used, because the first
        // parameter of the method receives the instance of Example.
        // When the delegate is invoked, only the second parameter is
        // required. 
        //
        OneParameter<int, int> invoke = (OneParameter<int, int>)
            multiplyHidden.CreateDelegate(
                typeof(OneParameter<int, int>), 
                new Example(42)
            );

        Console.WriteLine("3 * test = {0}", invoke(3));
    }
}
/* This code example produces the following output:

123456789 squared = 15241578750190521
3 * test = 126
 */


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

  • Er werden keine weiteren Assemblyverweise benötigt.

  • 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.

Fanden Sie dies hilfreich?
(1500 verbleibende Zeichen)
Vielen Dank für Ihr Feedback.

Community-Beiträge

HINZUFÜGEN
Anzeigen:
© 2014 Microsoft