• .NET 4.0 Dynamic Language Reflection Emit


    转载自: 小鱼游游

    首先,让我们快速的回顾一下,什么是反射以及反射可以被用来做什么。从第一部分内容中,你已经知道,反 射是在运行时发现对象的相关信息,并且执行这些对象(创建对象实例,执行对象上的方法)。这个功能是由.NETSystem.Reflection命名空间的类所提供的。这些被用于类型发现和动态调用的类包括:AssemblyModuleConstructorInfoMethodInfo以及其它。简单的说,它们不仅允许你浏览一个程序集暴露的类、方法、属性和字段,而且还允许你创建 一个类型的实例以及执行这些类型上的方法(调用成员)。这些特性对于在运行时对象发现,已经很了不起了,但.NET的反射机制并没有到此结束。反射还允 许你在运行时构建一个程序集,并且可以创建全新的类型。这就是反射发出(reflection emit)。


    何谓反射发出(
    Reflection Emit

     


    System..Reflection.Emit命名空间嵌套在System.Reflection的下面,它是,允许你从零开始,动态的构建程序集和类型的所有框架类的根。在需要时动态的产生代 码,类似这样的操作,虽然很少的开发人员会需要,但这对于.NET框架是一种凭据,证明有这样的工具可以解决有这样需求的业务问题。


    注意:反射发出(
    reflection emit)并不能产生源代码。换句话说,你在这里的努力并不能创建VB.Net或者C#代码。相 反,反射发出(reflection emit)类会创建MSIL op代码。


    作为例子,使用反射发出(
    reflection emit)可能会是这 样子的:

    1.         创建一个新的程序集(程序集是动态的存在于内存中或把它们持久化到磁盘上)。

    2.         在程序集内部,创建一个模块(module)。

    3.         在模块内部,创建一个类型。

    4.         给类型添加属 性和方法。

    5.         产生属性和方法内部的代码

    确切得说,当你使用Reflection.Emit类产生代码时,以上描述的是你实际中要遵循的过程。


    代码生成的过程

     


    依照上面列出的步 骤,让我们探讨一下构建一个程序集,必要的操作。为此,我们举个非常简单的例子。假设你想构建一个类
    MathOps,它有一个公共的方法(函 数),这个方法接收两个Integer类型的参数,然后返回它们的相加后的值。


    第 一步:构建程序集

     


    稍微扩充一下上面 列出的步骤,在实际的操作中,第一步更像是如下所述:

    a)         创建一个AssemblyName(用于唯一标识和命名程序集)。

    b)        获取当前应用程序域的一个引用(使用应用程序域提供的方法,返回AssemblyBuilder对象)。

    c)        通 过调用AppDomain.DefineDynamicAssembly产生一个AssemblyBuilder对象实例。

    为了开始程序集的构建过程,你首先需要创建一个AssemblyName实例,用于标识你的 程序集。如下:


    AssemblyName name = new AssemblyName();
    name.Name 
    = "MyAssembly";


    接下来,你需要System.AppDomain类 的一个实例。你可以从当前运行的线程实例中获取。


    AppDomain ad = System.Threading.Thread.GetDomain();


    这两个实例创建以后,你现在就可以定义一个AssemblyBuilder变 量,然后使用之前创建的AssemblyName和AppDomain的 实例把它实例化。AssemblyBuilder类是整个反射发出的工作支架。它给你,从零开始构 造一个新的程序集提供了主要的机制。除此之外,你还需要指定一个AssemblyBuilderAccess枚 举值,它将表明,你是想把程序集写入磁盘,保存到内存,还是两者都有。在这个例子里,你想把程序集保存在内存里。


    AssemblyBuilder builder;
    builder 
    = ad.DefineDynamicAssembly(name,AssemblyBuilderAccess.Run);


    第二 步:定义一个模块(module)

     



    在第二步里,你将使用ModuleBuilder类,在你之前创建的 程序集(builder)里创建一个动态的模块。ModuleBuilder用 于在一个程序集中创建一个模块。调用AssemblyBuilder对象上的DefineDynamicModule方法将会返回一个ModuleBuilder对 象实例。跟程序集一样,你必须给这个模块命名(在这里,名字仅仅是个字符窜)。

    ModuleBuilder mb;
    mb 
    = builder.DefineDynamicModule("MyModule");


     

    第三步:创建一个类型

     


    现在,你已经拥有 了一个程序集,一个模块,你可以把你的类添加到这个程序集中。为了创建一个新的类型,你需要使用TypeBuilder类。 你将会使用一个方法(DefineType)从“父对象”(指mb)得到一个TypeBuilder对象的实例。


     

    TypeBuilder theClass;
    theClass = mb.DefineType("MathOps",TypeAttributes.Public & TypeAttributes.Class);



    注意,你已经使用TypeAttributes枚举指定了该类型的可见度为公共的。


    第四步:添加一个方法

     


    既然所需的类型已经创建好了,那么你就可以给它添加方法了。即将添加的方法命名为ReturnSum,可见度为公共的。

    使用MethodBuilder类可以为你指 定的类型定义方法。你可以在之前创建的类型对象上(theClass)调用DefineMethod获取一个MethodBuilder实 例的引用。DefineMethod携带四个参数:方法的名称,方法可能的属性(如:public,private等等),方法的参数以及方法的返回值。在子程序里,参数和返回值可以是void值。对于这个例子里即将创建的方法,你同时需要指定参数和返回值的类型。

    为了定义返回值的类型,创 建一个包含返回类型值的类型对象(一个System.Int32类型的值)


    //返回值
    Type ret = typeof(System.Int32);


    使用类型值数组定义方法的参数,这两个参数也是Int32的类型值。


    //参数
    Type[] param = new Type[2];
    param[
    0= typeof(System.Int32);
    param[
    1= typeof(System.Int32);


    有了这些值,你现在就可以调用DefineMethod方法了。


    MethodBuilder methodBuilder;
    methodBuilder 
    = theClass.DefineMethod("ReturnSum",MethodAttributes.Public,ret,param);




    第五步:产生代码

     


    截止到现在,在第四步里,方法的框架已基本上搭建起来,你现在需要做的是添加方法的内部代 码。这是使用反射发出(reflection emit)产生代码的过程中真正核心的部分。

    有一点是需要注意的,反射 发出(reflection emit)的类不能产生源代码。换句话说,这里的结果并不会产生Visual Basic.NET或者C#代码,而是 产生MSIL op 代码。MSIL(微软 中间语言)是一种接近于汇编程序的中间代码语言。当.NET JIT 编译器产生本地二进制代码的 时候,就需要编译MSIL。Op代码是低级 的,类似于汇编程序的操作指令。


    考虑方法ReturnSum的如下实现:

    public int ReturnSum(int val1,int val2)
    {
        
    return val1 + val2;
    }


    如果你想“发出”这一段代码,你首先需要知道如何仅使用MSIL op代码编写这个方法。值得高兴的是,这里有一个快速,简单的办法可以做到。你可以简单的编译一下 这段代码,然后使用.NET框架里的实用工具ildasm.exe查 看程序集的结果。以下MSIL版本的代码是编译上面的方法产生的:


    .method public hidebysig instance int32  ReturnSum(int32 val1,
                                                       int32 val2) cil managed
    {
      
    // 代码大小       8 (0x8)
      .maxstack  2
      .locals init ([
    0] int32 CS$00000003$00000000)
      IL_0000:  ldarg.
    1
      IL_0001:  ldarg.
    2
      IL_0002:  add
      IL_0003:  stloc.
    0
      IL_0004:  br.s       IL_0006
      IL_0006:  ldloc.
    0
      IL_0007:  ret
    }
     // end of method Class1::ReturnSum


    为了产生这段代码,你需要使用ILGenerator类。你可以调用MethodBuilder.GetILGenerator()方法获取对应方法上的ILGenerator类的一个实例。换句话说,如下的代码将会获取你的方法ReturnMethod上的一个ILGenerator实 例。



    ILGenerator gen = methodBuilder.GetILGenerator();



    使用gen对 象,你可以把op指令注入到你的方法里。


    gen.Emit(OpCodes.Ldarg_1);
    gen.Emit(OpCodes.Ldarg_2);
    gen.Emit(OpCodes.Add);
    gen.Emit(OpCodes.Stloc_0);
    gen.Emit(OpCodes.Br_S);
    gen.Emit(OpCodes.Ldloc_0);
    gen.Emit(OpCodes.Ret);


    到此,你已经创建了方法,类,模块和程序集。为了得到这个类的一个引用,你可以调用CreateType, 类似于下面的代码:

    theClass.CreateType();


    命名空间和类

     

    正如你知道的那样,Reflection.Emit命 名空间包含一系列核心“构建”类,它们用于创建类型和与新类型相关的,如:各种特性,方法,字段,属性等等。Table 1描述了使用反射产生代码用到的主要的类。


    Table 1 反射发出相关类的参考

    Namespace.Class

    System.Reflection.Emit.AssemblyBuilder

    主要用途

    定义动态的.NET程序集:一种自我描述的 .NET内建块.动态程序集是通过反射发出特意产生的. 该类继承于System.Reflection.Assembly.

    范例

    Dim ab As AssemblyBuilderDim ad As AppDomainad = Thread.GetDomain()ab = ad.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run)

    Namespace.Class

    System.Reflection.Emit.ConstructorBuilder

    主要用途

    用于创建和声明一个动态类的构造器.它囊括了有关构造器的所有信息,包括:名称,方法签名和主体代码.仅仅在你需要创建一个带参数的构造器或者需要覆盖父类构造器的 默认行为的时候.

    范例

    Dim ourClass As TypeBuilder = [module].DefineType("ourClass", _TypeAttributes.Public)Dim ctorArgs As Type() = {GetType(String)}Dim ctor As ConstructorBuilder = _ourClass.DefineConstructor(MethodAttributes.Public, _CallingConventions.Standard, constructorArgs)

    Namespace.Class

    System.Reflection.Emit.CustomAttributeBuilder

    主要用途

    用于创建动态类的自定义特性.

    Namespace.Class

    System.Reflection.Emit.EnumBuilder

    主要用途

    定义和声明枚举.

    Namespace.Class

    System.Reflection.Emit.EventBuilder

    主要用途

    为动态类创建事件.

    Namespace.Class

    System.Reflection.Emit.FieldBuilder

    主要用途

    为动态类创建字段.

    Namespace.Class

    System.Reflection.Emit.ILGenerator

    主要用途

    用于产生MSIL代码.

    范例

    Dim gen As ILGenerator = someMethod.GetILGenerator()gen.Emit(OpCodes.Ldarg_0)gen.Emit(OpCodes.Ret)

    Namespace.Class

    System.Reflection.Emit.LocalBuilder

    主要用途

    创建方法或构造器的局部变量.

    Namespace.Class

    System.Reflection.Emit.MethodBuilder

    主要用途

    用于创建和声明动态类的方法.

    Namespace.Class

    System.Reflection.Emit.MethodRental

    主要用途

    一个很实用的类,用于从别的类中交换一个方法到动态创建的类中。当你需要快速重建一个已经在其它地方存在的方法时, 就显得非常有用。

    Namespace.Class

    System.Reflection.Emit.ParameterBuilder

    主要用途

    为方法的签名创建参数.

    Namespace.Class

    System.Reflection.Emit.PropertyBuilder

    主要用途

    为动态的类型创建属性.


  • 相关阅读:
    python斐波那契数列
    python装饰器
    Python文件操作
    python 第三方库的安装方法
    Ajax 基本使用学习记录
    微分方程概述
    Typora中的数学公式
    Ubuntu20.04 安装 mysql8.0 之后 root 账户无法登录
    Ubuntu20.04换源之后依旧慢?如何更有效的换源
    Ubuntu18.04更新python版本
  • 原文地址:https://www.cnblogs.com/cwy173/p/1831589.html
Copyright © 2020-2023  润新知