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

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


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

一、示例

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

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_ide

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;         }     }

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_加载_02

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

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

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_构造函数_03

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


二、编码

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

1. 字段


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


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

2. 属性

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_Dynamic/Proxy/Emit_04

//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);

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_构造函数_05

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

3. 构造函数

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_加载_06

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);

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_Dynamic/Proxy/Emit_07

4. 方法

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_构造函数_08

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);

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_Dynamic/Proxy/Emit_09

5. 到这里, 主要的部分就介绍完了, 下面给出检验的部分:

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_ide_10

//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));

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_Dynamic/Proxy/Emit_11

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

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

以下是完整的代码

Emit学习 - OpCodes - 动态添加属性、构造函数、方法_字段_12Emit学习 - OpCodes - 动态添加属性、构造函数、方法_加载_13

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

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