Emit学习(3) - OpCodes - 动态添加属性、构造函数、方

上一篇介绍了 IL 的部分, 基础的部分, 暂时就介绍到那里了, 接下来要进入代码编写阶段了.

今天的主题是 在代码运行的过程中, 去动态的创建类, 属性, 方法.

来源:http://www.cnblogs.com/yingql/archive/2009/03/22/1419149.html

废话不多说了, 直接上示例

一、示例

我这边所用的示例跟来源中是一样的,请看代码:

public class Add
    {

        int numA = 0;
        public int NumA  {  get { return numA; }  set { numA = value; }  }

        int numB = 0;
        public int NumB  { get { return NumB; } set { NumB = value; } }

        public Add(int a, int b)
        {
            numA = a;
            numB = b;
        }

        public int Calc()
        {
            return numA + numB;
        }
    }

我们要动态的创建这么一个类, 先不给你们看编码过程, 先给你们看结果吧, 先知道下, 这么做, 能大致得到一个什么结果.

运行代码之后, 会在bin里面得到一个 Elvin.dll 文件, 把这个文件和 ConsoleApplication2.exe 一起拿去反编译, 去看里面 Add 类的内容

图片 1

比较下来, 原生的和动态生成的稍有不同, 里面的IL代码也会有部分不同,这个是正常的, 虽然是参照着反编译的代码写的, 但是只是参照, 不是完全相同的.

 

二、编码

先看几个主要的部分, 后面我会把完整的代码贴出来

  1. 字段

    private int numA; --> .field private int32 numA

    var fieldABuilder = typeBldr.DefineField("numA", typeof(Int32), FieldAttributes.Private); //fieldABuilder.SetConstant(0); 此处为副初始值, 这里可省略

没什么好解释的, 一眼就能看懂, 至于这个 typeBldr 暂且不去管它

  1. 属性

    //1.属性 public int NumA { get{return numA;} set{numA = value;} }

             var propertyABuilder = typeBldr.DefineProperty("NumA", PropertyAttributes.None, 
                 CallingConventions.HasThis, typeof(Int32), null);
    
             //2. 定义 get, set 方法
    

            

             //2.1 get方法 代码部分和上图上的是有些不同的, 是参照着上图这个来的
             var getPropertyABuilder = typeBldr.DefineMethod("get", MethodAttributes.Public | 
                 MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(Int32), Type.EmptyTypes);
             //ILGenerator
             var getAIL = getPropertyABuilder.GetILGenerator();
             getAIL.Emit(OpCodes.Ldarg_0);   //this
             getAIL.Emit(OpCodes.Ldfld, fieldABuilder); //numA
             getAIL.Emit(OpCodes.Ret); //return numA
    

              

            

             //2.2 set方法 代码部分和上图中是不一样的, 是参照着上图来的
             var setPropertyABuilder = typeBldr.DefineMethod("set", MethodAttributes.Public | 
                 MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(Int32) });
             //ILGenerator
             var setAIL = setPropertyABuilder.GetILGenerator();
             //setAIL.Emit(OpCodes.Nop);   //这句可省略
             setAIL.Emit(OpCodes.Ldarg_0);  //this
             setAIL.Emit(OpCodes.Ldarg_1);  //value
             setAIL.Emit(OpCodes.Stfld, fieldABuilder); //numA = value;
             setAIL.Emit(OpCodes.Ret);   //return;
    
             //3.绑定get,set方法到属性上
             propertyABuilder.SetGetMethod(getPropertyABuilder);
             propertyABuilder.SetSetMethod(setPropertyABuilder);
    

很多时候, 我们用的是匿名的属性, 如: public int NumA{get;set;}, 这种情况, 会自动生成一个匿名私有变量来代替numA, 去与之匹配, 属性的本质是方法, 并不能用来存储数据

  1. 构造函数

    IL代码: .method public hidebysig specialname rtspecialname instance void .ctor(int32 a, int32 b) cil managed {

     .maxstack 8
     L_0000: ldarg.0 
     L_0001: ldc.i4.0 
     L_0002: stfld int32 ConsoleApplication2.Add::numA
     L_0007: ldarg.0 
     L_0008: ldc.i4.0 
     L_0009: stfld int32 ConsoleApplication2.Add::numB
     L_000e: ldarg.0 
     L_000f: call instance void [mscorlib]System.Object::.ctor()
     L_0014: nop 
     L_0015: nop 
     L_0016: ldarg.0 
     L_0017: ldarg.1 
     L_0018: stfld int32 ConsoleApplication2.Add::numA
     L_001d: ldarg.0 
     L_001e: ldarg.2 
     L_001f: stfld int32 ConsoleApplication2.Add::numB
     L_0024: nop 
     L_0025: ret 
    

    }

      //定义构造函数 ConstructorBuilder

             var constructorBuilder = typeBldr.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, new Type[] { typeof(Int32), typeof(Int32) });
             var ctorIL = constructorBuilder.GetILGenerator();
             // numA = a;
             ctorIL.Emit(OpCodes.Ldarg_0);
             ctorIL.Emit(OpCodes.Ldarg_1);
             ctorIL.Emit(OpCodes.Stfld, fieldABuilder);
             //NumB = b;
             ctorIL.Emit(OpCodes.Ldarg_0);
             ctorIL.Emit(OpCodes.Ldarg_2);
             ctorIL.Emit(OpCodes.Stfld, fieldBBuilder);
             ctorIL.Emit(OpCodes.Ret);
    
  2. 方法

    IL代码: .method public hidebysig instance int32 Calc() cil managed {

     .maxstack 2
     .locals init (
         [0] int32 num)
     L_0000: nop 
     L_0001: ldarg.0 
     L_0002: ldfld int32 ConsoleApplication2.Add::numA
     L_0007: ldarg.0 
     L_0008: ldfld int32 ConsoleApplication2.Add::numB
     L_000d: add 
     L_000e: stloc.0 
     L_000f: br.s L_0011
     L_0011: ldloc.0 
     L_0012: ret 
    

    }

              //8.定义方法 MethodBuilder
             var calcMethodBuilder = typeBldr.DefineMethod("Calc", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(Int32), Type.EmptyTypes);
             var calcIL = calcMethodBuilder.GetILGenerator();
             //加载私有字段numA
             calcIL.Emit(OpCodes.Ldarg_0);
             calcIL.Emit(OpCodes.Ldfld, fieldABuilder);
             //加载属性NumB
             calcIL.Emit(OpCodes.Ldarg_0);
             calcIL.Emit(OpCodes.Ldfld, fieldBBuilder);
             //想加并返回栈顶的值
             calcIL.Emit(OpCodes.Add);
             calcIL.Emit(OpCodes.Ret);
    
  3. 到这里, 主要的部分就介绍完了, 下面给出检验的部分:

    //9.结果

             Type type = typeBldr.CreateType();
             int a = 2;
             int b = 3;
             Object ob = Activator.CreateInstance(type, new object[] { a, b });
             Console.WriteLine("The Result of {0} + {1} is {2}", type.GetProperty("NumA").GetValue(ob), type.GetProperty("NumB").GetValue(ob), ob.GetType().GetMethod("Calc").Invoke(ob, null));
    

执行之后, 会输出  "The Result of 2 + 3 is 5"

至此, 今天要介绍的部分就结束了, 我把自己学习的过程和结果贴出来, 供有兴趣的童鞋瞄一下.

以下是完整的代码

图片 2图片 3

static void Main(string[] args)
        {
            //1.构建程序集
            var asmName = new AssemblyName("Elvinle");
            var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);

            //2.创建模块 
            var mdlBldr = asmBuilder.DefineDynamicModule("Elvin", "Elvin.dll");

            //3.定义类, public class Add
            var typeBldr = mdlBldr.DefineType("Add", TypeAttributes.Public | TypeAttributes.BeforeFieldInit);

            //4. 定义属性和字段
            //4.1字段 FieldBuilder
            var fieldABuilder = typeBldr.DefineField("numA", typeof(Int32), FieldAttributes.Private);
            //fieldABuilder.SetConstant(0); 此处为副初始值, 这里可省略

            var fieldBBuilder = typeBldr.DefineField("numB", typeof(Int32), FieldAttributes.Private);

            //4.2属性 PropertyBuilder
            var propertyABuilder = typeBldr.DefineProperty("NumA", PropertyAttributes.None, CallingConventions.HasThis, typeof(Int32), null);

            var propertyBBuilder = typeBldr.DefineProperty("NumB", PropertyAttributes.None, CallingConventions.HasThis, typeof(Int32), null);

            //5.定义属性numA的get;set;方法 MethodBuilder
            //5.1 get方法
            var getPropertyABuilder = typeBldr.DefineMethod("get", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(Int32), Type.EmptyTypes);
            //ILGenerator
            GetPropertyIL(getPropertyABuilder, fieldABuilder);
            //var getAIL = getPropertyABuilder.GetILGenerator();
            //getAIL.Emit(OpCodes.Ldarg_0);   //this
            //getAIL.Emit(OpCodes.Ldfld, fieldABuilder); //numA
            //getAIL.Emit(OpCodes.Ret); //return numA

            //5.2 set方法
            var setPropertyABuilder = typeBldr.DefineMethod("set", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(Int32) });
            //ILGenerator
            SetPropertyIL(setPropertyABuilder, fieldABuilder);
            //var setAIL = setPropertyABuilder.GetILGenerator();
            ////setAIL.Emit(OpCodes.Nop);   //这句可省略
            //setAIL.Emit(OpCodes.Ldarg_0);  //this
            //setAIL.Emit(OpCodes.Ldarg_1);  //value
            //setAIL.Emit(OpCodes.Stfld, fieldABuilder); //numA = value;
            //setAIL.Emit(OpCodes.Ret);   //return;

            //5.3 绑定
            propertyABuilder.SetGetMethod(getPropertyABuilder);
            propertyABuilder.SetSetMethod(setPropertyABuilder);

            //6.定义属性numA的get;set;方法 MethodBuilder
            var getPropertyBBuilder = typeBldr.DefineMethod("get", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(Int32), Type.EmptyTypes);
            GetPropertyIL(getPropertyBBuilder, fieldBBuilder);

            var setPropertyBBuilder = typeBldr.DefineMethod("set", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(Int32) });
            SetPropertyIL(setPropertyBBuilder, fieldBBuilder);

            propertyBBuilder.SetGetMethod(getPropertyBBuilder);
            propertyBBuilder.SetSetMethod(setPropertyBBuilder);

            //7.定义构造函数 ConstructorBuilder
            var constructorBuilder = typeBldr.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, new Type[] { typeof(Int32), typeof(Int32) });
            var ctorIL = constructorBuilder.GetILGenerator();
            // numA = a;
            ctorIL.Emit(OpCodes.Ldarg_0);
            ctorIL.Emit(OpCodes.Ldarg_1);
            ctorIL.Emit(OpCodes.Stfld, fieldABuilder);
            //NumB = b;
            ctorIL.Emit(OpCodes.Ldarg_0);
            ctorIL.Emit(OpCodes.Ldarg_2);
            ctorIL.Emit(OpCodes.Stfld, fieldBBuilder);
            ctorIL.Emit(OpCodes.Ret);

            //8.定义方法 MethodBuilder
            var calcMethodBuilder = typeBldr.DefineMethod("Calc", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(Int32), Type.EmptyTypes);
            var calcIL = calcMethodBuilder.GetILGenerator();
            //加载私有字段numA
            calcIL.Emit(OpCodes.Ldarg_0);
            calcIL.Emit(OpCodes.Ldfld, fieldABuilder);
            //加载属性NumB
            calcIL.Emit(OpCodes.Ldarg_0);
            calcIL.Emit(OpCodes.Ldfld, fieldBBuilder);
            //相加并返回栈顶的值
            calcIL.Emit(OpCodes.Add);
            calcIL.Emit(OpCodes.Ret);

            //9.结果
            Type type = typeBldr.CreateType();
            int a = 2;
            int b = 3;
            Object ob = Activator.CreateInstance(type, new object[] { a, b });
            Console.WriteLine("The Result of {0} + {1} is {2}", type.GetProperty("NumA").GetValue(ob), type.GetProperty("NumB").GetValue(ob), ob.GetType().GetMethod("Calc").Invoke(ob, null));

            asmBuilder.Save("Elvin.dll");
            Console.ReadKey();
        }

        private static void GetPropertyIL(MethodBuilder getPropertyBuilder, FieldBuilder fieldBuilder)
        {
            //ILGenerator
            var getAIL = getPropertyBuilder.GetILGenerator();
            getAIL.Emit(OpCodes.Ldarg_0);   //this
            getAIL.Emit(OpCodes.Ldfld, fieldBuilder); //numA
            getAIL.Emit(OpCodes.Ret); //return numA
        }

        private static void SetPropertyIL(MethodBuilder setPropertyBuilder, FieldBuilder fieldBuilder)
        {
            //ILGenerator
            var setAIL = setPropertyBuilder.GetILGenerator();
            //setAIL.Emit(OpCodes.Nop);   //这句可省略
            setAIL.Emit(OpCodes.Ldarg_0);  //this
            setAIL.Emit(OpCodes.Ldarg_1);  //value
            setAIL.Emit(OpCodes.Stfld, fieldBuilder); //numA = value;
            setAIL.Emit(OpCodes.Ret);   //return;
        }

View Code

希望我在学习的过程中, 也能带给你们一些不同的东西!

 

本文由金沙官网线上发布于编程,转载请注明出处:Emit学习(3) - OpCodes - 动态添加属性、构造函数、方

您可能还会对下面的文章感兴趣: