在Silverlight中使用DynamicMethod(动态方法)

     DynamicMethod 类(位于System.Reflection.Emit名空间下), 用于定义并表示一种可编译、执行和
丢弃的动态方法。
 
     而下面是微软对于DynamicMethod的应用及其运行情况的介绍:
    
    可以使用 DynamicMethod 类在运行时生成和执行方法,而不必生成动态程序集和动态类型来包含该方
法。回收 DynamicMethod 对象时,由实时 (JIT) 编译器创建的可执行代码也将回收。动态方法是生成和
执行少量代码的最有效方式。
    动态方法在逻辑上与模块或类型关联。如果与模块关联,动态方法对于该模块在全局范围内有效。如果有
足够的权限,动态方法可以跳过 JIT 编译器的可见性检查,访问具有该模块所声明类型的私有数据。可以将
动态方法与任何模块关联,无论该模块是否由您创建。
    如果动态方法与类型关联,动态方法可以访问该类型的私有成员。除非动态方法需要访问在同一模块中声
明的其他类型的私有数据,否则无需跳过 JIT 可见性检查。可以将动态方法与任何类型关联。
    下表显示了动态方法与模块关联或与模块中的类型关联时,在进行以及不进行 JIT 可见性检查的情况下,
动态方法可以在模块中访问的类型成员。 
  



    动态方法对所关联的模块或包含所关联的类型的模块具有权限。 
    无需对动态方法及其参数进行命名,但是可以指定名称以协助调试(下文中将会介绍)。动态方法或其属性
不支持自定义属性。

     这么一大块看下来,头肯定大了,而本文的例子确很简单,因为DEMO的主要代码全部取自Silverlight2 Beta2
CHM, 本人也只是将其中的代码修饰一下并将主要的注释翻译了过来,希望能通过注释让大家明白动态方法到底如
何写,如何用.当然因为DynamicMethod这个类的构造方法被重载了六次,而CHM中所介绍的也只是它的基本构造
方法, 形如:   

DynamicMethod(string name, Type returnType, Type[] parameterTypes);

    其中:
    name 就是动态方法的名称,它可为"",但不能是null;
    returnType 为动态方法的返回值类型;
    parameterTypes 为参数的类型数组;
   

    首先请大家看一下DEMO的运行截图如下:

       
    
    
    而这个DEMO的主要代码就是Example.cs, 其代码如下(相关内容详见注释):  

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

public class Example
{
    
// 下面的构造函数和公有属性用于将动态方法绑定到对象实例时使用
    public int Test;
    
public Example(int test)
    {
        
this.Test = test;
    }

    
// 下面的代理(delegate)用于调用动态方法
    private delegate long Square(int input);
    
private delegate int Bound(int input);
    
private delegate int Unbound(Example target, int input);

    
/// <summary>
    
/// 动态方法简单调用
    
/// </summary>
    
/// <param name="outputBlock"></param>
    public static void Demo1(System.Windows.Controls.TextBlock outputBlock)
    {
        outputBlock.Text 
+= "例子 1 : 动态方法简单调用\n\n";

        
// 例子 1: 简单的动态方法
        
// 创建一个动态方法所使用的参数类型数组,因为当前例子中只有一个参数(int 类型),所
        
// 以数组中将会只有一个类型
        Type[] methodArgs = { typeof(int) };

        
// 创建一个动态方法(DynamicMethod)。
        
// 在这个例子中, 方法名称为SquareIt(平方运算).
        
// 另外,动态方法名称不是必填项. 因为系统不会按方法名称来调用动态方法(而是采用delegate).
        
// 还有就是两个动态方法可以使用同一个方法名称.不过,这个方法名称会在调用堆栈(calls stacks)
        
// 中出现,这样便于进行调试
        
//
        
// 在这个例中,动态方法的返回类型是Long [如下typeof(long)]. 
        System.Reflection.Emit.DynamicMethod squareIt = new System.Reflection.Emit.DynamicMethod(
            
"SquareIt",
            
typeof(long),
            methodArgs
        );

        
// 下面是SquareIt(平方运算)的函数内容.
        
// 在这个例子中, 使用ILGenerator生成 MSIL(MS中间语言). 
        
// DynamicMethod has an associated type DynamicILInfo that can be used in conjunction with 
        
// unmanaged code generators.
        
//
        
// MSIL 加载该参数(Integer)到堆栈上 , 并将其转换成为Long 类型. duplicates(复制) 栈顶元素.
        
// 然后将栈顶的两个元素的乘积(平方运算)压入堆栈(返回结果时使用)
        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);

        
// 为上面的方法创建一个代码并将其绑定到Square的实例上. 
        
// Creating the delegate completes the method, and any further 
        
// attempts to change the method (for example, by adding more
        
// MSIL) are ignored. 
        
//
        Square invokeSquareIt =
            (Square)squareIt.CreateDelegate(
typeof(Square));

        
// 下面的代码显示了如使用 Square delegate,

        outputBlock.Text 
+= String.Format("123456789 squared = {0}\n",
            invokeSquareIt(
123456789));

        
// 当然绑定代理也可以使用 Func (能生成带单一参数且有返回类型的泛型代理),如下:
        Func<intlong> invokeGeneric =
            (Func
<intlong>)squareIt.CreateDelegate(typeof(Func<intlong>));

        outputBlock.Text 
+= String.Format("987654321 squared = {0}\n",
            invokeGeneric(
987654321));

    }

    
/// <summary>
    
/// 对象实例绑定调用
    
/// </summary>
    
/// <param name="outputBlock"></param>
    public static void Demo2(System.Windows.Controls.TextBlock outputBlock)
    {
        outputBlock.Text 
+= "\n例子 2: 对象实例绑定调用\n\n";

        
// 例子 2: 将动态方法绑定到实例上.
        
//
        
// 下面语句将会创建一个指定参数类型的数组,以便将其绑定的动态方法上
        
// 如要将方法(method)代理绑定到对象上,那么第一个参数应匹配代理要绑定的对象类型
        
// 在下面代码中,要绑定的是Example类型的对象
        Type[] methodArgs2 = { typeof(Example), typeof(int) };

        
// 创建动态方法, 在这个例子中, 该(动态)方法没有指定名称(为""), 
        
// 返回值类型为Integer. 这个方法将会访问Example类的公有成员(Test)
        
//
        System.Reflection.Emit.DynamicMethod multiplyTestField = new System.Reflection.Emit.DynamicMethod(
            
"",
            
typeof(int),
            methodArgs2
        );

        
// 在这个例子中, 使用ILGenerator生成 MSIL(MS中间语言). 
        
//
        
// MSIL 加载第一个参数(Example类的一个实例), 然后使用这个实例来访问公有成员(Test) 
        ILGenerator ilMP = multiplyTestField.GetILGenerator();
        ilMP.Emit(OpCodes.Ldarg_0);

        FieldInfo testInfo 
= typeof(Example).GetField("Test",
            BindingFlags.Public 
| BindingFlags.Instance);

        
// 加载第二个参数, 然后这两个数字会被相乘,如果返回值比INT大,则会执行截取操作,并将结果返回.
        ilMP.Emit(OpCodes.Ldfld, testInfo);
        ilMP.Emit(OpCodes.Ldarg_1);
        ilMP.Emit(OpCodes.Mul);
        ilMP.Emit(OpCodes.Ret);


        
#region Bound1

        
// 创建上面动态方法的代理. 
        
// Creating the delegate completes the method, and any further 
        
// attempts to change the method  for example, by adding more
        
// MSIL  are ignored. 
        
// 
        
// 下面代码会将上面的动态方法绑定到Example 类的一个新实例上,同时将该实例的Test属性设置成42.
        
// 这样每次代理运行时,都会调用同一个Example实例.另外这个代理将不再使用类型Example作为参数,
        
// 因为Example的实例已绑定到了代理的Target参数上(如下面的((Example)invoke.Target).Test调用)
        
// 因为下面的方法调用就像是隐藏了方法的第一个参数一样.
        
// 
        
// 下面代理被执行了两次,分别使用了不同的参数值(分别为3, 5)
        Bound invoke = (Bound)multiplyTestField.CreateDelegate(
            
typeof(Bound), new Example(42));

        outputBlock.Text 
+= String.Format(
            
"Example.Test = {0}; {1} * Example.Test = {2}\n",
            ((Example)invoke.Target).Test, 
3, invoke(3));
        outputBlock.Text 
+= String.Format(
            
"Example.Test = {0}; {1} * Example.Test = {2}\n",
            ((Example)invoke.Target).Test, 
5, invoke(5));

        
//修改当前实例的Test字段(42*2=84)
        ((Example)invoke.Target).Test *= 2;

        outputBlock.Text 
+= String.Format(
            
"Example.Test = {0}; {1} * Example.Test = {2}\n\n",
            ((Example)invoke.Target).Test, 
5, invoke(5));

        
#endregion

        
#region Bound2
        
// 下面的Example实例将会被绑定到一个Bound代理上, 然后这个代理会运行两次,
        
// 其间对 Example的Test值进行加1操作(类似上面的*2操作)
        Example ex = new Example(5280);

        Bound another 
=
            (Bound)multiplyTestField.CreateDelegate(
typeof(Bound), ex);

        outputBlock.Text 
+= String.Format(
            
"Example.Test = {0}; {1} * Example.Test = {2}\n",
            ex.Test, 
3, another(3));

        ex.Test 
+= 1;

        outputBlock.Text 
+= String.Format(
            
"Example.Test = {0}; {1} * Example.Test = {2}\n\n",
            ex.Test, 
3, another(3));

        
#endregion


        
#region Unbound

        
// 最后,创建一个类型Unbound的代理.而它有两个参数,一个是Example的实例,另一个是一个Integer型数据.
        
// 下面的代理(Unbound) 将会绑定不带Example实例的方法,实相应的实例参数将会在代理执行过程中进行加载
        Unbound notBound =
            (Unbound)multiplyTestField.CreateDelegate(
typeof(Unbound));

        outputBlock.Text 
+= String.Format("{0} * Example.Test(42) = {1}\n",
            
10, notBound(new Example(42), 10));
        outputBlock.Text 
+= String.Format("{0} * Example.Test(56) = {1}\n",
            
10, notBound(new Example(56), 10));

        
#endregion
    }
}

    代码量不是很大,但又学习又翻译却用了一些时间,希望大家见谅:)    
    当然如下链接是它的一些在线文档:
    
    http://msdn.microsoft.com/zh-cn/library/system.reflection.emit.dynamicmethod(VS.85).aspx    
    http://msdn.microsoft.com/zh-cn/library/exczf7b9(en-us,VS.85).aspx    
    http://technet.microsoft.com/zh-cn/sysinternals/system.reflection.emit.dynamicmethod(VS.85).aspx    
    
    好了,今天的内容就先到这里了,呵呵:)    
    Demo源码包,请点击这里.
   

posted @ 2008-06-20 09:05  代震军  阅读(3173)  评论(2编辑  收藏  举报