使用英语阅读

通过


如何:定义和执行动态方法

以下过程演示如何定义和执行一个简单的动态方法和绑定到类实例的动态方法。 有关动态方法的详细信息,请参阅该 DynamicMethod 类。

  1. 声明要执行方法的委托类型。 请考虑使用泛型委托来最大程度地减少需要声明的委托类型数。 以下代码声明两种可用于该方法的 SquareIt 委托类型,其中一种是泛型的。

    private delegate long SquareItInvoker(int input);
    
    private delegate TReturn OneParameter<TReturn, TParameter0>
        (TParameter0 p0);
    
  2. 创建一个数组,指定动态方法的参数类型。 在此示例中,唯一 int 的参数是 (Integer 在 Visual Basic 中),因此数组只有一个元素。

    Type[] methodArgs = {typeof(int)};
    
  3. 创建一个 DynamicMethod。 在此示例中,该方法命名 SquareIt为 .

    备注

    不需要为动态方法命名,也不能按名称调用它们。 多个动态方法可以具有相同的名称。 但是,名称显示在调用堆栈中,可用于调试。

    返回值的类型指定为 long。 该方法与包含 Example 类的模块相关联,该模块包含示例代码。 可以指定任何已加载的模块。 动态方法类似于模块级 static 方法(Shared 在 Visual Basic 中)。

    DynamicMethod squareIt = new DynamicMethod(
        "SquareIt",
        typeof(long),
        methodArgs,
        typeof(Example).Module);
    
  4. 生成方法正文。 在此示例中,对象 ILGenerator 用于发出公共中间语言(CIL)。 或者, DynamicILInfo 对象可以与非托管代码生成器结合使用,以发出方法 DynamicMethod正文。

    在此示例中,CIL 将参数(即一个 int)加载到堆栈上,将其转换为一个 long,重复该 long,然后将这两个数字相乘。 这将把平方结果留在堆栈上,这个方法唯一需要执行的操作就是返回。

    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. 创建委托(在步骤 1 中声明)的实例,该实例通过调用 CreateDelegate 该方法来表示动态方法。 创建委托后,该步骤即告完成,任何想要进一步更改方法(例如添加更多中间语言代码 CIL)的尝试都会被忽略。 以下代码创建一个委托并使用泛型委托调用它。

    OneParameter<long, int> invokeSquareIt =
        (OneParameter<long, int>)
        squareIt.CreateDelegate(typeof(OneParameter<long, int>));
    
    Console.WriteLine($"123456789 squared = {invokeSquareIt(123456789)}");
    
  6. 声明要执行方法的委托类型。 请考虑使用泛型委托来最大程度地减少需要声明的委托类型数。 下面的代码声明一个泛型委托类型,该类型可用于使用一个参数和一个返回值执行任何方法;如果委托绑定到对象,则使用具有两个参数和返回值的方法。

    private delegate TReturn OneParameter<TReturn, TParameter0>
        (TParameter0 p0);
    
  7. 创建一个数组,指定动态方法的参数类型。 如果要将表示方法的委托绑定到对象,则第一个参数必须与委托所绑定类型相匹配。 在此示例中,有两个参数,类型为 Exampleint(在 Visual Basic 中为 Integer)。

    Type[] methodArgs2 = { typeof(Example), typeof(int) };
    
  8. 创建一个 DynamicMethod。 在此示例中,该方法没有名称。 返回值的类型指定为 intInteger 在 Visual Basic 中)。 该方法能够访问Example类的私有成员和受保护成员。

    DynamicMethod multiplyHidden = new DynamicMethod(
        "",
        typeof(int),
        methodArgs2,
        typeof(Example));
    
  9. 生成方法体。 在此示例中,对象 ILGenerator 用于发出公共中间语言(CIL)。 或者,可以将 DynamicILInfo 对象与非托管代码生成器结合使用,以生成 DynamicMethod 的方法正文。

    此示例中的 CIL 加载第一个参数,该参数是类的 Example 实例,并使用它加载类型的 int私有实例字段的值。 加载第二个参数,两个数字相乘。 如果结果大于 int,该值将被截断,并且丢弃最重要的位。 方法返回,并将返回值置于堆栈上。

    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);
    
  10. 创建委托(在步骤 1 中声明)的实例,该实例通过调用 CreateDelegate(Type, Object) 方法重载来表示动态方法。 创建委托会完成方法的定义,并忽略任何进一步更改该方法的尝试(例如添加更多 CIL)。

    备注

    可以多次调用 CreateDelegate 该方法,以创建绑定到目标类型其他实例的委托。

    以下代码将方法绑定到其专用测试字段设置为 42 的 Example 类的新实例。 也就是说,每次调用委托时,Example 的实例都会被传递给方法的第一个参数。

    由于方法的第一个参数始终接收 Example 的实例,因此使用委托 OneParameter。 调用委托时,只需要第二个参数。

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

示例:

下面的代码示例演示了一个简单的动态方法和绑定到类实例的动态方法。

简单的动态方法采用一个参数(一个 32 位整数)并返回该整数的 64 位正方形。 泛型委托用于调用该方法。

第二个动态方法具有两个参数,类型 Example 和类型 int,(Integer 在 Visual Basic 中)。 创建动态方法后,它将绑定到Example的实例,使用一个以int类型为参数的泛型委托。 委托没有类型为Example的参数,因为方法的第一个参数始终接收绑定的Example实例。 调用委托时,仅提供 int 参数。 此动态方法访问类的 Example 私有字段,并返回专用字段和参数的 int 乘积。

该代码示例定义可用于执行方法的委托。

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 = {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 = {invoke(3)}");
    }
}
/* This code example produces the following output:

123456789 squared = 15241578750190521
3 * test = 126
 */

另请参阅