首先需要声明一个程序集名称,
1 // specify a new assembly name
2 var assemblyName = new AssemblyName("Kitty");
从当前应用程序域获取程序集构造器,
1 // create assembly builder
2 var assemblyBuilder = AppDomain.CurrentDomain
3 .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
有几种动态程序集构造访问限制:
- AssemblyBuilderAccess.Run; 表示程序集可被执行,但不能被保存。
- AssemblyBuilderAccess.Save; 表示程序集可被保存,但不能被执行。
- AssemblyBuilderAccess.RunAndSave; 表示程序集可被保存并能被执行。
- AssemblyBuilderAccess.ReflectionOnly; 表示程序集只能用于反射上下文环境中,不能被执行。
- AssemblyBuilderAccess.RunAndCollect; 表示程序集可以被卸载并且内存会被回收。
在程序集中构造动态模块,
1 // create module builder
2 var moduleBuilder = assemblyBuilder.DefineDynamicModule("KittyModule", "Kitty.exe");
模块即是代码的集合,一个程序集中可以有多个模块。并且理论上讲,每个模块可以使用不同的编程语言实现,例如C#/VB。
构造一个类型构造器,
1 // create type builder for a class
2 var typeBuilder = moduleBuilder.DefineType("HelloKittyClass", TypeAttributes.Public);
通过类型构造器定义一个方法,获取方法构造器,获得方法构造器的IL生成器,通过编写IL代码来定义方法功能。
1 // create method builder
2 var methodBuilder = typeBuilder.DefineMethod(
3 "SayHelloMethod",
4 MethodAttributes.Public | MethodAttributes.Static,
5 null,
6 null);
7
8 // then get the method il generator
9 var il = methodBuilder.GetILGenerator();
10
11 // then create the method function
12 il.Emit(OpCodes.Ldstr, "Hello, Kitty!");
13 il.Emit(OpCodes.Call,
14 typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
15 il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine"));
16 il.Emit(OpCodes.Pop); // we just read something here, throw it.
17 il.Emit(OpCodes.Ret);
创建类型,
1 // then create the whole class type
2 var helloKittyClassType = typeBuilder.CreateType();
如果当前程序集是可运行的,则设置一个程序入口,
1 // set entry point for this assembly
2 assemblyBuilder.SetEntryPoint(helloKittyClassType.GetMethod("SayHelloMethod"));
将动态生成的程序集保存成磁盘文件,
1 // save assembly
2 assemblyBuilder.Save("Kitty.exe");
此时,通过反编译工具,将Kitty.exe反编译成代码,
1 using System;
2
3 public class HelloKittyClass
4 {
5 public static void SayHelloMethod()
6 {
7 Console.WriteLine("Hello, Kitty!");
8 Console.ReadLine();
9 }
10 }
运行结果,

完整代码

1 using System;
2 using System.Reflection;
3 using System.Reflection.Emit;
4
5 namespace EmitIntroduction
6 {
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 // specify a new assembly name
12 var assemblyName = new AssemblyName("Kitty");
13
14 // create assembly builder
15 var assemblyBuilder = AppDomain.CurrentDomain
16 .DefineDynamicAssembly(assemblyName,
17 AssemblyBuilderAccess.RunAndSave);
18
19 // create module builder
20 var moduleBuilder =
21 assemblyBuilder.DefineDynamicModule(
22 "KittyModule", "Kitty.exe");
23
24 // create type builder for a class
25 var typeBuilder =
26 moduleBuilder.DefineType(
27 "HelloKittyClass", TypeAttributes.Public);
28
29 // create method builder
30 var methodBuilder = typeBuilder.DefineMethod(
31 "SayHelloMethod",
32 MethodAttributes.Public | MethodAttributes.Static,
33 null,
34 null);
35
36 // then get the method il generator
37 var il = methodBuilder.GetILGenerator();
38
39 // then create the method function
40 il.Emit(OpCodes.Ldstr, "Hello, Kitty!");
41 il.Emit(OpCodes.Call,
42 typeof(Console).GetMethod(
43 "WriteLine", new Type[] { typeof(string) }));
44 il.Emit(OpCodes.Call,
45 typeof(Console).GetMethod("ReadLine"));
46 il.Emit(OpCodes.Pop); // we just read something here, throw it.
47 il.Emit(OpCodes.Ret);
48
49 // then create the whole class type
50 var helloKittyClassType = typeBuilder.CreateType();
51
52 // set entry point for this assembly
53 assemblyBuilder.SetEntryPoint(
54 helloKittyClassType.GetMethod("SayHelloMethod"));
55
56 // save assembly
57 assemblyBuilder.Save("Kitty.exe");
58
59 Console.WriteLine(
60 "Hi, Dennis, a Kitty assembly has been generated for you.");
61 Console.ReadLine();
62 }
63 }
64 }

下载完整代码
进一步阅读使用Emit生成构造函数和属性
在aop编程概念介绍中,常见的示例为拦截对象,并在对象的某方法执行前和执行后分别记录日志。
而最常用的拦截方式是使用动态代理类,用其封装一个日志拦截器,当方法被执行时进行日志记录。
日志拦截器类
public class interceptor
{
public object invoke(object @object, string @method, object[] parameters)
{
console.writeline(
string.format("interceptor does something before invoke [{0}]...", @method));
var retobj = @object.gettype().getmethod(@method).invoke(@object, parameters);
console.writeline(
string.format("interceptor does something after invoke [{0}]...", @method));
return retobj;
}
}
被拦截对象类
假设我们有一个command类,包含一个方法execute用于执行一些工作。
public class command
{
public virtual void execute()
{
console.writeline("command executing...");
console.writeline("hello kitty!");
console.writeline("command executed.");
}
}
我们需要在execute方法执行前和执行后分别记录日志。
动态代理类
1 public class proxy
2 {
3 public static t of<t>() where t : class, new()
4 {
5 string nameofassembly = typeof(t).name + "proxyassembly";
6 string nameofmodule = typeof(t).name + "proxymodule";
7 string nameoftype = typeof(t).name + "proxy";
8
9 var assemblyname = new assemblyname(nameofassembly);
10 var assembly = appdomain.currentdomain
11 .definedynamicassembly(assemblyname, assemblybuilderaccess.run);
12 var modulebuilder = assembly.definedynamicmodule(nameofmodule);
13
14 var typebuilder = modulebuilder.definetype(
15 nameoftype, typeattributes.public, typeof(t));
16
17 injectinterceptor<t>(typebuilder);
18
19 var t = typebuilder.createtype();
20
21 return activator.createinstance(t) as t;
22 }
23
24 private static void injectinterceptor<t>(typebuilder typebuilder)
25 {
26 // ---- define fields ----
27
28 var fieldinterceptor = typebuilder.definefield(
29 "_interceptor", typeof(interceptor), fieldattributes.private);
30
31 // ---- define costructors ----
32
33 var constructorbuilder = typebuilder.defineconstructor(
34 methodattributes.public, callingconventions.standard, null);
35 var ilofctor = constructorbuilder.getilgenerator();
36
37 ilofctor.emit(opcodes.ldarg_0);
38 ilofctor.emit(opcodes.newobj, typeof(interceptor).getconstructor(new type[0]));
39 ilofctor.emit(opcodes.stfld, fieldinterceptor);
40 ilofctor.emit(opcodes.ret);
41
42 // ---- define methods ----
43
44 var methodsoftype = typeof(t).getmethods(bindingflags.public | bindingflags.instance);
45
46 for (var i = 0; i < methodsoftype.length; i++)
47 {
48 var method = methodsoftype[i];
49 var methodparametertypes =
50 method.getparameters().select(p => p.parametertype).toarray();
51
52 var methodbuilder = typebuilder.definemethod(
53 method.name,
54 methodattributes.public | methodattributes.virtual,
55 callingconventions.standard,
56 method.returntype,
57 methodparametertypes);
58
59 var ilofmethod = methodbuilder.getilgenerator();
60 ilofmethod.emit(opcodes.ldarg_0);
61 ilofmethod.emit(opcodes.ldfld, fieldinterceptor);
62
63 // create instance of t
64 ilofmethod.emit(opcodes.newobj, typeof(t).getconstructor(new type[0]));
65 ilofmethod.emit(opcodes.ldstr, method.name);
66
67 // build the method parameters
68 if (methodparametertypes == null)
69 {
70 ilofmethod.emit(opcodes.ldnull);
71 }
72 else
73 {
74 var parameters = ilofmethod.declarelocal(typeof(object[]));
75 ilofmethod.emit(opcodes.ldc_i4, methodparametertypes.length);
76 ilofmethod.emit(opcodes.newarr, typeof(object));
77 ilofmethod.emit(opcodes.stloc, parameters);
78
79 for (var j = 0; j < methodparametertypes.length; j++)
80 {
81 ilofmethod.emit(opcodes.ldloc, parameters);
82 ilofmethod.emit(opcodes.ldc_i4, j);
83 ilofmethod.emit(opcodes.ldarg, j + 1);
84 ilofmethod.emit(opcodes.stelem_ref);
85 }
86 ilofmethod.emit(opcodes.ldloc, parameters);
87 }
88
89 // call invoke() method of interceptor
90 ilofmethod.emit(opcodes.callvirt, typeof(interceptor).getmethod("invoke"));
91
92 // pop the stack if return void
93 if (method.returntype == typeof(void))
94 {
95 ilofmethod.emit(opcodes.pop);
96 }
97
98 // complete
99 ilofmethod.emit(opcodes.ret);
100 }
101 }
102 }
使用动态代理类
class program
{
static void main(string[] args)
{
var command = proxy.of<command>();
command.execute();
console.writeline("hi, dennis, great, we got the interceptor works.");
console.readline();
}
}
运行结果

完整代码

1 using system;
2 using system.linq;
3 using system.reflection;
4 using system.reflection.emit;
5
6 namespace emitcreatedynamicproxy
7 {
8 class program
9 {
10 static void main(string[] args)
11 {
12 var command = proxy.of<command>();
13 command.execute();
14
15 console.writeline("hi, dennis, great, we got the interceptor works.");
16 console.readline();
17 }
18 }
19
20 public class command
21 {
22 public virtual void execute()
23 {
24 console.writeline("command executing...");
25 console.writeline("hello kitty!");
26 console.writeline("command executed.");
27 }
28 }
29
30 public class interceptor
31 {
32 public object invoke(object @object, string @method, object[] parameters)
33 {
34 console.writeline(
35 string.format("interceptor does something before invoke [{0}]...", @method));
36
37 var retobj = @object.gettype().getmethod(@method).invoke(@object, parameters);
38
39 console.writeline(
40 string.format("interceptor does something after invoke [{0}]...", @method));
41
42 return retobj;
43 }
44 }
45
46 public class proxy
47 {
48 public static t of<t>() where t : class, new()
49 {
50 string nameofassembly = typeof(t).name + "proxyassembly";
51 string nameofmodule = typeof(t).name + "proxymodule";
52 string nameoftype = typeof(t).name + "proxy";
53
54 var assemblyname = new assemblyname(nameofassembly);
55 var assembly = appdomain.currentdomain
56 .definedynamicassembly(assemblyname, assemblybuilderaccess.run);
57 var modulebuilder = assembly.definedynamicmodule(nameofmodule);
58
59 var typebuilder = modulebuilder.definetype(
60 nameoftype, typeattributes.public, typeof(t));
61
62 injectinterceptor<t>(typebuilder);
63
64 var t = typebuilder.createtype();
65
66 return activator.createinstance(t) as t;
67 }
68
69 private static void injectinterceptor<t>(typebuilder typebuilder)
70 {
71 // ---- define fields ----
72
73 var fieldinterceptor = typebuilder.definefield(
74 "_interceptor", typeof(interceptor), fieldattributes.private);
75
76 // ---- define costructors ----
77
78 var constructorbuilder = typebuilder.defineconstructor(
79 methodattributes.public, callingconventions.standard, null);
80 var ilofctor = constructorbuilder.getilgenerator();
81
82 ilofctor.emit(opcodes.ldarg_0);
83 ilofctor.emit(opcodes.newobj, typeof(interceptor).getconstructor(new type[0]));
84 ilofctor.emit(opcodes.stfld, fieldinterceptor);
85 ilofctor.emit(opcodes.ret);
86
87 // ---- define methods ----
88
89 var methodsoftype = typeof(t).getmethods(bindingflags.public | bindingflags.instance);
90
91 for (var i = 0; i < methodsoftype.length; i++)
92 {
93 var method = methodsoftype[i];
94 var methodparametertypes =
95 method.getparameters().select(p => p.parametertype).toarray();
96
97 var methodbuilder = typebuilder.definemethod(
98 method.name,
99 methodattributes.public | methodattributes.virtual,
100 callingconventions.standard,
101 method.returntype,
102 methodparametertypes);
103
104 var ilofmethod = methodbuilder.getilgenerator();
105 ilofmethod.emit(opcodes.ldarg_0);
106 ilofmethod.emit(opcodes.ldfld, fieldinterceptor);
107
108 // create instance of t
109 ilofmethod.emit(opcodes.newobj, typeof(t).getconstructor(new type[0]));
110 ilofmethod.emit(opcodes.ldstr, method.name);
111
112 // build the method parameters
113 if (methodparametertypes == null)
114 {
115 ilofmethod.emit(opcodes.ldnull);
116 }
117 else
118 {
119 var parameters = ilofmethod.declarelocal(typeof(object[]));
120 ilofmethod.emit(opcodes.ldc_i4, methodparametertypes.length);
121 ilofmethod.emit(opcodes.newarr, typeof(object));
122 ilofmethod.emit(opcodes.stloc, parameters);
123
124 for (var j = 0; j < methodparametertypes.length; j++)
125 {
126 ilofmethod.emit(opcodes.ldloc, parameters);
127 ilofmethod.emit(opcodes.ldc_i4, j);
128 ilofmethod.emit(opcodes.ldarg, j + 1);
129 ilofmethod.emit(opcodes.stelem_ref);
130 }
131 ilofmethod.emit(opcodes.ldloc, parameters);
132 }
133
134 // call invoke() method of interceptor
135 ilofmethod.emit(opcodes.callvirt, typeof(interceptor).getmethod("invoke"));
136
137 // pop the stack if return void
138 if (method.returntype == typeof(void))
139 {
140 ilofmethod.emit(opcodes.pop);
141 }
142
143 // complete
144 ilofmethod.emit(opcodes.ret);
145 }
146 }
147 }
148 }
view code
下载完整代码
搜索此文相关文章:C#使用Emit构造拦截器动态代理类
此文链接:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%8E%A8%E8%8D%90/30505.shtml
转载请注明出处:C#使用Emit构造拦截器动态代理类 - 博客园
一、System.Reflection.Emit概述
Emit,可以称为发出或者产生。与Emit相关的类基本都存在于System.Reflection.Emit命名空间下。反射,我们可以取得形如程序集包含哪些类型,类型包含哪些方法等等大量的信息,而Emit则可以在运行时动态生成代码。
二、IL代码解析
以下代码为例:

1 static void Main(string[] args)
2 {
3 int i = 1;
4 int j = 2;
5 int k = 3;
6 Console.WriteLine(i+j+k);
7 }
翻译文IL代码为:


1 .method private hidebysig static void Main(string[] args) cil managed
2 {
3 .entrypoint //程序入口
4 // Code size 19 (0x13)
5 .maxstack 3 //定义函数代码所用堆栈的最大深度,也可理解为Call Stack的变量个数
6
7 //以下我们把它看做是完成代码中的初始化
8 .locals init (int32 V_0,int32 V_1,int32 V_2) //定义 int 类型参数 V_0,V_1,V_2 (此时已经把V_0,V_1,V_2存入了Call Stack中)
9 IL_0000: nop //即No Operation 没有任何操作,我们也不用管它
10
11 IL_0001: ldc.i4.1 //加载第一个变量"i" (压入Evaluation Stack中)
12 IL_0002: stloc.0 //把"i"赋值给Call Stack中第0个位置(V_0)
13 IL_0003: ldc.i4.2 //加载第二个变量"j" (压入Evaluation Stack中)
14 IL_0004: stloc.1 //把"j"赋值给Call Stack中第1个位置(V_1)
15 IL_0005: ldc.i4.3 //加载第三个变量"k" (压入Evaluation Stack中)
16 IL_0006: stloc.2 //把 "k" 赋值给Call Stack中第2个位置(V_2)
17
18 //上面代码初始化完成后要开始输出了,所要把数据从Call Stack中取出
19
20 IL_0007: ldloc.0 //取Call Stack中位置为0的元素(V_0)的值("i"的值) (相当于Copy一份值Call Stack中V_0的值。V_0本身的值是不变的)
21 IL_0008: ldloc.1 //取Call Stack中位置为1的元素(V_1)的值("j"的值) (同上)
22 IL_0009: add // 做加法操作
23 IL_000a: ldloc.2 // 取出Call Stack中位置为2的元素(V_2)的值("k"的值)
24 IL_000b: add // 做加法操作
25 IL_000c: call void [mscorlib]System.Console::WriteLine(int32) //调用输出方法
26 IL_0011: nop
27 IL_0012: ret //即为 return 标记 返回值
28 } // end of method Program::Main

指令详解
Managed Heap::这是动态配置(Dynamic Allocation)的记忆体,由 Garbage Collector(GC)在执行时自动管理,整个 Process 共用一个Managed Heap(我理解为托管堆,存储引用类型的值)。
Evaluation Stack:这是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Evaluation Stack(我理解为类似一个临时存放值类型数据的线程栈)
Call Stack:这个是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Call Stack。每呼叫一次 method,就会使得 Call Stack 上多了一個个 Record Frame;呼叫完成之後,此 Record Frame 会被丢弃(我理解为一个局部变量表,用于存放.locals init(int32 V_0)指令的参数值如:V_0)
.maxstack:代码中变量需要在Call Stack 中占用几个位置
.locals init (int32 V_0,int32 V_1,int32 V_2):定义变量并存入Call Stack中
nop:即No Operation 没有任何操作,我们也不用管它,
ldstr.:即Load String 把字符串加压入Evaluation Stack中
stloc.:把Evaluation Stack中的值弹出赋值到Call Stack中
ldloc.:把Call Stack中指定位置的值取出(copy)存入 Evaluation Stack中 以上两条指令为相互的操作stloc赋值,ldloc取值
call: 调用指定的方法
ret: 即return 标记返回
二、动态生成代码
首先我们需要了解每个动态类型在.net中都是用什么类型来表示的。
程序集:System.Reflection.Emit.AssemblyBuilder(定义并表示动态程序集)
构造函数:System.Reflection.Emit.ConstructorBuilder(定义并表示动态类的构造函数)
自定义属性:System.Reflection.Emit.CustomAttributeBuilder(帮助生成自定义属性 使用构造函数传递的参数来生成类的属性)
枚举:System.Reflection.Emit.EnumBuilder(说明并表示枚举类型)
事件:System.Reflection.Emit.EventBuilder(定义类的事件)
字段:System.Reflection.Emit.FieldBuilder(定义并表示字段。无法继承此类)
局部变量:System.Reflection.Emit.LocalBuilder(表示方法或构造函数内的局部变量)
方法:System.Reflection.Emit.MethodBuilder(定义并表示动态类的方法(或构造函数))
模块:System.Reflection.Emit.ModuleBuilder(定义和表示动态程序集中的模块)
参数:System.Reflection.Emit.ParameterBuilder(创建或关联参数信息 如:方法参数,事件参数等)
属性:System.Reflection.Emit.PropertyBuilder(定义类型的属性 (Property))
类:System.Reflection.Emit.TypeBuilder(在运行时定义并创建类的新实例)
以下介绍Emit生成代码的基本流程:
1.构建程序集
在创建程序集之前,我们先要为它取个名字。
var asmName = new AssemblyName("Test");
AssemblyName位于System.Reflection命名空间下,它代表程序集的名称。
然后我们就可以用上面的名字来创建一个程序集了:
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
AssemblyBuilderAccess.ReflectionOnly:
DefineDynamicAssembly有很多重载,比如上面的例子可以添加第三个参数用于作为生成的程序集要存放到的目录。关于其他重载形式,大家可以查阅MSDN。这里重点说说AssemblyBuilderAccess这个枚举。
它有以下几个值:
AssemblyBuilderAccess.ReflectionOnly:表示动态程序集只能用于反射获取元素据用,不能执行。
AssemblyBuilderAccess.Run:表示动态程序集是用于执行的。
AssemblyBuilderAccess.Save:表示动态程序集会被保存到磁盘上,不能立即执行。
AssemblyBuilderAccess.RunAndSave:表示动态程序集会被保存至磁盘并能立即执行。
2.创建模块
创建程序集后,就需要为程序集添加模块了,我们可以如下定义一个模块:
var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.dll");
如果想把动态生成的程序集保存至磁盘(如本例),定义模块时模块所在文件的名称一定要和保存程序集(后面会提到)时提供的文件名称一样。
3.定义类
有了前面的准备工作,我们开始定义我们的类型:
var typeBldr = mdlBldr.DefineType("Hello",TypeAttributes.Public);
DefineType还可以设置要定义的类的基类,要实现的接口等等。
4.定义类成员(方法,属性等等)
既然有了类,下面我们就为它添加一个SayHello方法吧:
1 var methodBldr = typeBldr.DefineMethod(
2 "SayHello",
3 MethodAttributes.Public,
4 null,//return type
5 null//parameter type
6 );
该方法的原型为public void SayHell();
方法签名已经生成好了,但方法还缺少实现。在生成方法的实现前,必须提及一个很重要的概念:evaluation stack。在.Net下基本所有的操作都是通过入栈出栈完成的。这个栈就是evaluation stack。比如要计算两个数(a,b)的和,首先要将a放入evaluation stack中,然后再将b也放入栈中,最后执行加法时将弹出栈顶的两个元素也就是a和b,相加再将结果推送至栈顶。
Console.WriteLine("Hello,World")可以用Emit这样生成:
1 var il = methodBldr.GetILGenerator();//获取il生成器
2
3 il.Emit(OpCodes.Ldstr,"Hello, World");
4
5 il.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(string)}));
6
7 il.Emit(OpCodes.Ret);
OpCodes枚举定义了所有可能的操作,这里用到了:
ldStr:加载一个字符串到evaluation stack。
Call:调用方法。
Ret:返回,当evaluation stack有值时会返回栈顶值。
完成上面的步骤,一个类型好像就已经完成了。事实上却还没有,最后我们还必须显示的调用CreateType来完成类型的创建。
typeBldr.CreateType();
这样一个完整的类就算完成了。但为了能用reflector查看我们创建的动态程序集,我们选择将这个程序集保存下来。
asmBuilder.Save("Main.dll");
如前面定义模块时所说,这里文件名字必须和模块保存到的文件一致,否则我们前面定义的模块和这个模块的一切就无家可归了。接下来,(如果在定义模块时未指定动态创建的程序要保存到哪个目录)我们就可以到 Debug目录下看看生成的Main.dll了,用Reflector打开可以看到:

三、不包含main的控制台程序
一直以来,应用程序(控制台,winform)都是从Main函数启动的,如果没有Main还能启动吗?答案是可以,下面就用emit来做这样一个控制台程序,完整代码如下:

1 var asmName = new AssemblyName("Test");
2
3 var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
4
5 asmName,
6
7 AssemblyBuilderAccess.RunAndSave);
8
9 var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.exe");
10
11 var typeBldr = mdlBldr.DefineType("Hello", TypeAttributes.Public);
12
13 var methodBldr = typeBldr.DefineMethod(
14
15 "SayHello",
16
17 MethodAttributes.Public | MethodAttributes.Static,
18
19 null,//return type
20
21 null//parameter type
22
23 );
24
25 var il = methodBldr.GetILGenerator();//获取il生成器
26
27 il.Emit(OpCodes.Ldstr,"Hello, World");
28
29 il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{typeof(string)}));
30
31 il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine"));
32
33 il.Emit(OpCodes.Pop);//读入的值会被推送至evaluation stack,而本方法是没有返回值的,因此,需要将栈上的值抛弃
34
35 il.Emit(OpCodes.Ret);
36
37 var t = typeBldr.CreateType();
38
39 asmBuilder.SetEntryPoint(t.GetMethod("SayHello"));
40
41 asmBuilder.Save("Main.exe");
首先需要声明一个程序集名称,
1 // specify a new assembly name
2 var assemblyName = new AssemblyName("Kitty");
从当前应用程序域获取程序集构造器,
1 // create assembly builder
2 var assemblyBuilder = AppDomain.CurrentDomain
3 .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
有几种动态程序集构造访问限制:
- AssemblyBuilderAccess.Run; 表示程序集可被执行,但不能被保存。
- AssemblyBuilderAccess.Save; 表示程序集可被保存,但不能被执行。
- AssemblyBuilderAccess.RunAndSave; 表示程序集可被保存并能被执行。
- AssemblyBuilderAccess.ReflectionOnly; 表示程序集只能用于反射上下文环境中,不能被执行。
- AssemblyBuilderAccess.RunAndCollect; 表示程序集可以被卸载并且内存会被回收。
在程序集中构造动态模块,
1 // create module builder
2 var moduleBuilder = assemblyBuilder.DefineDynamicModule("KittyModule", "Kitty.exe");
模块即是代码的集合,一个程序集中可以有多个模块。并且理论上讲,每个模块可以使用不同的编程语言实现,例如C#/VB。
构造一个类型构造器,
1 // create type builder for a class
2 var typeBuilder = moduleBuilder.DefineType("HelloKittyClass", TypeAttributes.Public);
通过类型构造器定义一个方法,获取方法构造器,获得方法构造器的IL生成器,通过编写IL代码来定义方法功能。
1 // create method builder
2 var methodBuilder = typeBuilder.DefineMethod(
3 "SayHelloMethod",
4 MethodAttributes.Public | MethodAttributes.Static,
5 null,
6 null);
7
8 // then get the method il generator
9 var il = methodBuilder.GetILGenerator();
10
11 // then create the method function
12 il.Emit(OpCodes.Ldstr, "Hello, Kitty!");
13 il.Emit(OpCodes.Call,
14 typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
15 il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine"));
16 il.Emit(OpCodes.Pop); // we just read something here, throw it.
17 il.Emit(OpCodes.Ret);
创建类型,
1 // then create the whole class type
2 var helloKittyClassType = typeBuilder.CreateType();
如果当前程序集是可运行的,则设置一个程序入口,
1 // set entry point for this assembly
2 assemblyBuilder.SetEntryPoint(helloKittyClassType.GetMethod("SayHelloMethod"));
将动态生成的程序集保存成磁盘文件,
1 // save assembly
2 assemblyBuilder.Save("Kitty.exe");
此时,通过反编译工具,将Kitty.exe反编译成代码,
1 using System;
2
3 public class HelloKittyClass
4 {
5 public static void SayHelloMethod()
6 {
7 Console.WriteLine("Hello, Kitty!");
8 Console.ReadLine();
9 }
10 }
运行结果,

完整代码

1 using System;
2 using System.Reflection;
3 using System.Reflection.Emit;
4
5 namespace EmitIntroduction
6 {
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 // specify a new assembly name
12 var assemblyName = new AssemblyName("Kitty");
13
14 // create assembly builder
15 var assemblyBuilder = AppDomain.CurrentDomain
16 .DefineDynamicAssembly(assemblyName,
17 AssemblyBuilderAccess.RunAndSave);
18
19 // create module builder
20 var moduleBuilder =
21 assemblyBuilder.DefineDynamicModule(
22 "KittyModule", "Kitty.exe");
23
24 // create type builder for a class
25 var typeBuilder =
26 moduleBuilder.DefineType(
27 "HelloKittyClass", TypeAttributes.Public);
28
29 // create method builder
30 var methodBuilder = typeBuilder.DefineMethod(
31 "SayHelloMethod",
32 MethodAttributes.Public | MethodAttributes.Static,
33 null,
34 null);
35
36 // then get the method il generator
37 var il = methodBuilder.GetILGenerator();
38
39 // then create the method function
40 il.Emit(OpCodes.Ldstr, "Hello, Kitty!");
41 il.Emit(OpCodes.Call,
42 typeof(Console).GetMethod(
43 "WriteLine", new Type[] { typeof(string) }));
44 il.Emit(OpCodes.Call,
45 typeof(Console).GetMethod("ReadLine"));
46 il.Emit(OpCodes.Pop); // we just read something here, throw it.
47 il.Emit(OpCodes.Ret);
48
49 // then create the whole class type
50 var helloKittyClassType = typeBuilder.CreateType();
51
52 // set entry point for this assembly
53 assemblyBuilder.SetEntryPoint(
54 helloKittyClassType.GetMethod("SayHelloMethod"));
55
56 // save assembly
57 assemblyBuilder.Save("Kitty.exe");
58
59 Console.WriteLine(
60 "Hi, Dennis, a Kitty assembly has been generated for you.");
61 Console.ReadLine();
62 }
63 }
64 }

下载完整代码
进一步阅读使用Emit生成构造函数和属性
在aop编程概念介绍中,常见的示例为拦截对象,并在对象的某方法执行前和执行后分别记录日志。
而最常用的拦截方式是使用动态代理类,用其封装一个日志拦截器,当方法被执行时进行日志记录。
日志拦截器类
public class interceptor
{
public object invoke(object @object, string @method, object[] parameters)
{
console.writeline(
string.format("interceptor does something before invoke [{0}]...", @method));
var retobj = @object.gettype().getmethod(@method).invoke(@object, parameters);
console.writeline(
string.format("interceptor does something after invoke [{0}]...", @method));
return retobj;
}
}
被拦截对象类
假设我们有一个command类,包含一个方法execute用于执行一些工作。
public class command
{
public virtual void execute()
{
console.writeline("command executing...");
console.writeline("hello kitty!");
console.writeline("command executed.");
}
}
我们需要在execute方法执行前和执行后分别记录日志。
动态代理类
1 public class proxy
2 {
3 public static t of<t>() where t : class, new()
4 {
5 string nameofassembly = typeof(t).name + "proxyassembly";
6 string nameofmodule = typeof(t).name + "proxymodule";
7 string nameoftype = typeof(t).name + "proxy";
8
9 var assemblyname = new assemblyname(nameofassembly);
10 var assembly = appdomain.currentdomain
11 .definedynamicassembly(assemblyname, assemblybuilderaccess.run);
12 var modulebuilder = assembly.definedynamicmodule(nameofmodule);
13
14 var typebuilder = modulebuilder.definetype(
15 nameoftype, typeattributes.public, typeof(t));
16
17 injectinterceptor<t>(typebuilder);
18
19 var t = typebuilder.createtype();
20
21 return activator.createinstance(t) as t;
22 }
23
24 private static void injectinterceptor<t>(typebuilder typebuilder)
25 {
26 // ---- define fields ----
27
28 var fieldinterceptor = typebuilder.definefield(
29 "_interceptor", typeof(interceptor), fieldattributes.private);
30
31 // ---- define costructors ----
32
33 var constructorbuilder = typebuilder.defineconstructor(
34 methodattributes.public, callingconventions.standard, null);
35 var ilofctor = constructorbuilder.getilgenerator();
36
37 ilofctor.emit(opcodes.ldarg_0);
38 ilofctor.emit(opcodes.newobj, typeof(interceptor).getconstructor(new type[0]));
39 ilofctor.emit(opcodes.stfld, fieldinterceptor);
40 ilofctor.emit(opcodes.ret);
41
42 // ---- define methods ----
43
44 var methodsoftype = typeof(t).getmethods(bindingflags.public | bindingflags.instance);
45
46 for (var i = 0; i < methodsoftype.length; i++)
47 {
48 var method = methodsoftype[i];
49 var methodparametertypes =
50 method.getparameters().select(p => p.parametertype).toarray();
51
52 var methodbuilder = typebuilder.definemethod(
53 method.name,
54 methodattributes.public | methodattributes.virtual,
55 callingconventions.standard,
56 method.returntype,
57 methodparametertypes);
58
59 var ilofmethod = methodbuilder.getilgenerator();
60 ilofmethod.emit(opcodes.ldarg_0);
61 ilofmethod.emit(opcodes.ldfld, fieldinterceptor);
62
63 // create instance of t
64 ilofmethod.emit(opcodes.newobj, typeof(t).getconstructor(new type[0]));
65 ilofmethod.emit(opcodes.ldstr, method.name);
66
67 // build the method parameters
68 if (methodparametertypes == null)
69 {
70 ilofmethod.emit(opcodes.ldnull);
71 }
72 else
73 {
74 var parameters = ilofmethod.declarelocal(typeof(object[]));
75 ilofmethod.emit(opcodes.ldc_i4, methodparametertypes.length);
76 ilofmethod.emit(opcodes.newarr, typeof(object));
77 ilofmethod.emit(opcodes.stloc, parameters);
78
79 for (var j = 0; j < methodparametertypes.length; j++)
80 {
81 ilofmethod.emit(opcodes.ldloc, parameters);
82 ilofmethod.emit(opcodes.ldc_i4, j);
83 ilofmethod.emit(opcodes.ldarg, j + 1);
84 ilofmethod.emit(opcodes.stelem_ref);
85 }
86 ilofmethod.emit(opcodes.ldloc, parameters);
87 }
88
89 // call invoke() method of interceptor
90 ilofmethod.emit(opcodes.callvirt, typeof(interceptor).getmethod("invoke"));
91
92 // pop the stack if return void
93 if (method.returntype == typeof(void))
94 {
95 ilofmethod.emit(opcodes.pop);
96 }
97
98 // complete
99 ilofmethod.emit(opcodes.ret);
100 }
101 }
102 }
使用动态代理类
class program
{
static void main(string[] args)
{
var command = proxy.of<command>();
command.execute();
console.writeline("hi, dennis, great, we got the interceptor works.");
console.readline();
}
}
运行结果

完整代码

1 using system;
2 using system.linq;
3 using system.reflection;
4 using system.reflection.emit;
5
6 namespace emitcreatedynamicproxy
7 {
8 class program
9 {
10 static void main(string[] args)
11 {
12 var command = proxy.of<command>();
13 command.execute();
14
15 console.writeline("hi, dennis, great, we got the interceptor works.");
16 console.readline();
17 }
18 }
19
20 public class command
21 {
22 public virtual void execute()
23 {
24 console.writeline("command executing...");
25 console.writeline("hello kitty!");
26 console.writeline("command executed.");
27 }
28 }
29
30 public class interceptor
31 {
32 public object invoke(object @object, string @method, object[] parameters)
33 {
34 console.writeline(
35 string.format("interceptor does something before invoke [{0}]...", @method));
36
37 var retobj = @object.gettype().getmethod(@method).invoke(@object, parameters);
38
39 console.writeline(
40 string.format("interceptor does something after invoke [{0}]...", @method));
41
42 return retobj;
43 }
44 }
45
46 public class proxy
47 {
48 public static t of<t>() where t : class, new()
49 {
50 string nameofassembly = typeof(t).name + "proxyassembly";
51 string nameofmodule = typeof(t).name + "proxymodule";
52 string nameoftype = typeof(t).name + "proxy";
53
54 var assemblyname = new assemblyname(nameofassembly);
55 var assembly = appdomain.currentdomain
56 .definedynamicassembly(assemblyname, assemblybuilderaccess.run);
57 var modulebuilder = assembly.definedynamicmodule(nameofmodule);
58
59 var typebuilder = modulebuilder.definetype(
60 nameoftype, typeattributes.public, typeof(t));
61
62 injectinterceptor<t>(typebuilder);
63
64 var t = typebuilder.createtype();
65
66 return activator.createinstance(t) as t;
67 }
68
69 private static void injectinterceptor<t>(typebuilder typebuilder)
70 {
71 // ---- define fields ----
72
73 var fieldinterceptor = typebuilder.definefield(
74 "_interceptor", typeof(interceptor), fieldattributes.private);
75
76 // ---- define costructors ----
77
78 var constructorbuilder = typebuilder.defineconstructor(
79 methodattributes.public, callingconventions.standard, null);
80 var ilofctor = constructorbuilder.getilgenerator();
81
82 ilofctor.emit(opcodes.ldarg_0);
83 ilofctor.emit(opcodes.newobj, typeof(interceptor).getconstructor(new type[0]));
84 ilofctor.emit(opcodes.stfld, fieldinterceptor);
85 ilofctor.emit(opcodes.ret);
86
87 // ---- define methods ----
88
89 var methodsoftype = typeof(t).getmethods(bindingflags.public | bindingflags.instance);
90
91 for (var i = 0; i < methodsoftype.length; i++)
92 {
93 var method = methodsoftype[i];
94 var methodparametertypes =
95 method.getparameters().select(p => p.parametertype).toarray();
96
97 var methodbuilder = typebuilder.definemethod(
98 method.name,
99 methodattributes.public | methodattributes.virtual,
100 callingconventions.standard,
101 method.returntype,
102 methodparametertypes);
103
104 var ilofmethod = methodbuilder.getilgenerator();
105 ilofmethod.emit(opcodes.ldarg_0);
106 ilofmethod.emit(opcodes.ldfld, fieldinterceptor);
107
108 // create instance of t
109 ilofmethod.emit(opcodes.newobj, typeof(t).getconstructor(new type[0]));
110 ilofmethod.emit(opcodes.ldstr, method.name);
111
112 // build the method parameters
113 if (methodparametertypes == null)
114 {
115 ilofmethod.emit(opcodes.ldnull);
116 }
117 else
118 {
119 var parameters = ilofmethod.declarelocal(typeof(object[]));
120 ilofmethod.emit(opcodes.ldc_i4, methodparametertypes.length);
121 ilofmethod.emit(opcodes.newarr, typeof(object));
122 ilofmethod.emit(opcodes.stloc, parameters);
123
124 for (var j = 0; j < methodparametertypes.length; j++)
125 {
126 ilofmethod.emit(opcodes.ldloc, parameters);
127 ilofmethod.emit(opcodes.ldc_i4, j);
128 ilofmethod.emit(opcodes.ldarg, j + 1);
129 ilofmethod.emit(opcodes.stelem_ref);
130 }
131 ilofmethod.emit(opcodes.ldloc, parameters);
132 }
133
134 // call invoke() method of interceptor
135 ilofmethod.emit(opcodes.callvirt, typeof(interceptor).getmethod("invoke"));
136
137 // pop the stack if return void
138 if (method.returntype == typeof(void))
139 {
140 ilofmethod.emit(opcodes.pop);
141 }
142
143 // complete
144 ilofmethod.emit(opcodes.ret);
145 }
146 }
147 }
148 }
view code
下载完整代码
搜索此文相关文章:C#使用Emit构造拦截器动态代理类
此文链接:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%8E%A8%E8%8D%90/30505.shtml
转载请注明出处:C#使用Emit构造拦截器动态代理类 - 博客园
一、System.Reflection.Emit概述
Emit,可以称为发出或者产生。与Emit相关的类基本都存在于System.Reflection.Emit命名空间下。反射,我们可以取得形如程序集包含哪些类型,类型包含哪些方法等等大量的信息,而Emit则可以在运行时动态生成代码。
二、IL代码解析
以下代码为例:

1 static void Main(string[] args)
2 {
3 int i = 1;
4 int j = 2;
5 int k = 3;
6 Console.WriteLine(i+j+k);
7 }
翻译文IL代码为:


1 .method private hidebysig static void Main(string[] args) cil managed
2 {
3 .entrypoint //程序入口
4 // Code size 19 (0x13)
5 .maxstack 3 //定义函数代码所用堆栈的最大深度,也可理解为Call Stack的变量个数
6
7 //以下我们把它看做是完成代码中的初始化
8 .locals init (int32 V_0,int32 V_1,int32 V_2) //定义 int 类型参数 V_0,V_1,V_2 (此时已经把V_0,V_1,V_2存入了Call Stack中)
9 IL_0000: nop //即No Operation 没有任何操作,我们也不用管它
10
11 IL_0001: ldc.i4.1 //加载第一个变量"i" (压入Evaluation Stack中)
12 IL_0002: stloc.0 //把"i"赋值给Call Stack中第0个位置(V_0)
13 IL_0003: ldc.i4.2 //加载第二个变量"j" (压入Evaluation Stack中)
14 IL_0004: stloc.1 //把"j"赋值给Call Stack中第1个位置(V_1)
15 IL_0005: ldc.i4.3 //加载第三个变量"k" (压入Evaluation Stack中)
16 IL_0006: stloc.2 //把 "k" 赋值给Call Stack中第2个位置(V_2)
17
18 //上面代码初始化完成后要开始输出了,所要把数据从Call Stack中取出
19
20 IL_0007: ldloc.0 //取Call Stack中位置为0的元素(V_0)的值("i"的值) (相当于Copy一份值Call Stack中V_0的值。V_0本身的值是不变的)
21 IL_0008: ldloc.1 //取Call Stack中位置为1的元素(V_1)的值("j"的值) (同上)
22 IL_0009: add // 做加法操作
23 IL_000a: ldloc.2 // 取出Call Stack中位置为2的元素(V_2)的值("k"的值)
24 IL_000b: add // 做加法操作
25 IL_000c: call void [mscorlib]System.Console::WriteLine(int32) //调用输出方法
26 IL_0011: nop
27 IL_0012: ret //即为 return 标记 返回值
28 } // end of method Program::Main

指令详解
Managed Heap::这是动态配置(Dynamic Allocation)的记忆体,由 Garbage Collector(GC)在执行时自动管理,整个 Process 共用一个Managed Heap(我理解为托管堆,存储引用类型的值)。
Evaluation Stack:这是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Evaluation Stack(我理解为类似一个临时存放值类型数据的线程栈)
Call Stack:这个是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Call Stack。每呼叫一次 method,就会使得 Call Stack 上多了一個个 Record Frame;呼叫完成之後,此 Record Frame 会被丢弃(我理解为一个局部变量表,用于存放.locals init(int32 V_0)指令的参数值如:V_0)
.maxstack:代码中变量需要在Call Stack 中占用几个位置
.locals init (int32 V_0,int32 V_1,int32 V_2):定义变量并存入Call Stack中
nop:即No Operation 没有任何操作,我们也不用管它,
ldstr.:即Load String 把字符串加压入Evaluation Stack中
stloc.:把Evaluation Stack中的值弹出赋值到Call Stack中
ldloc.:把Call Stack中指定位置的值取出(copy)存入 Evaluation Stack中 以上两条指令为相互的操作stloc赋值,ldloc取值
call: 调用指定的方法
ret: 即return 标记返回
二、动态生成代码
首先我们需要了解每个动态类型在.net中都是用什么类型来表示的。
程序集:System.Reflection.Emit.AssemblyBuilder(定义并表示动态程序集)
构造函数:System.Reflection.Emit.ConstructorBuilder(定义并表示动态类的构造函数)
自定义属性:System.Reflection.Emit.CustomAttributeBuilder(帮助生成自定义属性 使用构造函数传递的参数来生成类的属性)
枚举:System.Reflection.Emit.EnumBuilder(说明并表示枚举类型)
事件:System.Reflection.Emit.EventBuilder(定义类的事件)
字段:System.Reflection.Emit.FieldBuilder(定义并表示字段。无法继承此类)
局部变量:System.Reflection.Emit.LocalBuilder(表示方法或构造函数内的局部变量)
方法:System.Reflection.Emit.MethodBuilder(定义并表示动态类的方法(或构造函数))
模块:System.Reflection.Emit.ModuleBuilder(定义和表示动态程序集中的模块)
参数:System.Reflection.Emit.ParameterBuilder(创建或关联参数信息 如:方法参数,事件参数等)
属性:System.Reflection.Emit.PropertyBuilder(定义类型的属性 (Property))
类:System.Reflection.Emit.TypeBuilder(在运行时定义并创建类的新实例)
以下介绍Emit生成代码的基本流程:
1.构建程序集
在创建程序集之前,我们先要为它取个名字。
var asmName = new AssemblyName("Test");
AssemblyName位于System.Reflection命名空间下,它代表程序集的名称。
然后我们就可以用上面的名字来创建一个程序集了:
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
AssemblyBuilderAccess.ReflectionOnly:
DefineDynamicAssembly有很多重载,比如上面的例子可以添加第三个参数用于作为生成的程序集要存放到的目录。关于其他重载形式,大家可以查阅MSDN。这里重点说说AssemblyBuilderAccess这个枚举。
它有以下几个值:
AssemblyBuilderAccess.ReflectionOnly:表示动态程序集只能用于反射获取元素据用,不能执行。
AssemblyBuilderAccess.Run:表示动态程序集是用于执行的。
AssemblyBuilderAccess.Save:表示动态程序集会被保存到磁盘上,不能立即执行。
AssemblyBuilderAccess.RunAndSave:表示动态程序集会被保存至磁盘并能立即执行。
2.创建模块
创建程序集后,就需要为程序集添加模块了,我们可以如下定义一个模块:
var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.dll");
如果想把动态生成的程序集保存至磁盘(如本例),定义模块时模块所在文件的名称一定要和保存程序集(后面会提到)时提供的文件名称一样。
3.定义类
有了前面的准备工作,我们开始定义我们的类型:
var typeBldr = mdlBldr.DefineType("Hello",TypeAttributes.Public);
DefineType还可以设置要定义的类的基类,要实现的接口等等。
4.定义类成员(方法,属性等等)
既然有了类,下面我们就为它添加一个SayHello方法吧:
1 var methodBldr = typeBldr.DefineMethod(
2 "SayHello",
3 MethodAttributes.Public,
4 null,//return type
5 null//parameter type
6 );
该方法的原型为public void SayHell();
方法签名已经生成好了,但方法还缺少实现。在生成方法的实现前,必须提及一个很重要的概念:evaluation stack。在.Net下基本所有的操作都是通过入栈出栈完成的。这个栈就是evaluation stack。比如要计算两个数(a,b)的和,首先要将a放入evaluation stack中,然后再将b也放入栈中,最后执行加法时将弹出栈顶的两个元素也就是a和b,相加再将结果推送至栈顶。
Console.WriteLine("Hello,World")可以用Emit这样生成:
1 var il = methodBldr.GetILGenerator();//获取il生成器
2
3 il.Emit(OpCodes.Ldstr,"Hello, World");
4
5 il.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(string)}));
6
7 il.Emit(OpCodes.Ret);
OpCodes枚举定义了所有可能的操作,这里用到了:
ldStr:加载一个字符串到evaluation stack。
Call:调用方法。
Ret:返回,当evaluation stack有值时会返回栈顶值。
完成上面的步骤,一个类型好像就已经完成了。事实上却还没有,最后我们还必须显示的调用CreateType来完成类型的创建。
typeBldr.CreateType();
这样一个完整的类就算完成了。但为了能用reflector查看我们创建的动态程序集,我们选择将这个程序集保存下来。
asmBuilder.Save("Main.dll");
如前面定义模块时所说,这里文件名字必须和模块保存到的文件一致,否则我们前面定义的模块和这个模块的一切就无家可归了。接下来,(如果在定义模块时未指定动态创建的程序要保存到哪个目录)我们就可以到 Debug目录下看看生成的Main.dll了,用Reflector打开可以看到:

三、不包含main的控制台程序
一直以来,应用程序(控制台,winform)都是从Main函数启动的,如果没有Main还能启动吗?答案是可以,下面就用emit来做这样一个控制台程序,完整代码如下:

1 var asmName = new AssemblyName("Test");
2
3 var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
4
5 asmName,
6
7 AssemblyBuilderAccess.RunAndSave);
8
9 var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.exe");
10
11 var typeBldr = mdlBldr.DefineType("Hello", TypeAttributes.Public);
12
13 var methodBldr = typeBldr.DefineMethod(
14
15 "SayHello",
16
17 MethodAttributes.Public | MethodAttributes.Static,
18
19 null,//return type
20
21 null//parameter type
22
23 );
24
25 var il = methodBldr.GetILGenerator();//获取il生成器
26
27 il.Emit(OpCodes.Ldstr,"Hello, World");
28
29 il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{typeof(string)}));
30
31 il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine"));
32
33 il.Emit(OpCodes.Pop);//读入的值会被推送至evaluation stack,而本方法是没有返回值的,因此,需要将栈上的值抛弃
34
35 il.Emit(OpCodes.Ret);
36
37 var t = typeBldr.CreateType();
38
39 asmBuilder.SetEntryPoint(t.GetMethod("SayHello"));
40
41 asmBuilder.Save("Main.exe");
一、System.Reflection.Emit概述
Emit,可以称为发出或者产生。与Emit相关的类基本都存在于System.Reflection.Emit命名空间下。反射,我们可以取得形如程序集包含哪些类型,类型包含哪些方法等等大量的信息,而Emit则可以在运行时动态生成代码。
二、IL代码解析
以下代码为例:

1 static void Main(string[] args)
2 {
3 int i = 1;
4 int j = 2;
5 int k = 3;
6 Console.WriteLine(i+j+k);
7 }
翻译文IL代码为:


1 .method private hidebysig static void Main(string[] args) cil managed
2 {
3 .entrypoint //程序入口
4 // Code size 19 (0x13)
5 .maxstack 3 //定义函数代码所用堆栈的最大深度,也可理解为Call Stack的变量个数
6
7 //以下我们把它看做是完成代码中的初始化
8 .locals init (int32 V_0,int32 V_1,int32 V_2) //定义 int 类型参数 V_0,V_1,V_2 (此时已经把V_0,V_1,V_2存入了Call Stack中)
9 IL_0000: nop //即No Operation 没有任何操作,我们也不用管它
10
11 IL_0001: ldc.i4.1 //加载第一个变量"i" (压入Evaluation Stack中)
12 IL_0002: stloc.0 //把"i"赋值给Call Stack中第0个位置(V_0)
13 IL_0003: ldc.i4.2 //加载第二个变量"j" (压入Evaluation Stack中)
14 IL_0004: stloc.1 //把"j"赋值给Call Stack中第1个位置(V_1)
15 IL_0005: ldc.i4.3 //加载第三个变量"k" (压入Evaluation Stack中)
16 IL_0006: stloc.2 //把 "k" 赋值给Call Stack中第2个位置(V_2)
17
18 //上面代码初始化完成后要开始输出了,所要把数据从Call Stack中取出
19
20 IL_0007: ldloc.0 //取Call Stack中位置为0的元素(V_0)的值("i"的值) (相当于Copy一份值Call Stack中V_0的值。V_0本身的值是不变的)
21 IL_0008: ldloc.1 //取Call Stack中位置为1的元素(V_1)的值("j"的值) (同上)
22 IL_0009: add // 做加法操作
23 IL_000a: ldloc.2 // 取出Call Stack中位置为2的元素(V_2)的值("k"的值)
24 IL_000b: add // 做加法操作
25 IL_000c: call void [mscorlib]System.Console::WriteLine(int32) //调用输出方法
26 IL_0011: nop
27 IL_0012: ret //即为 return 标记 返回值
28 } // end of method Program::Main

指令详解
Managed Heap::这是动态配置(Dynamic Allocation)的记忆体,由 Garbage Collector(GC)在执行时自动管理,整个 Process 共用一个Managed Heap(我理解为托管堆,存储引用类型的值)。
Evaluation Stack:这是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Evaluation Stack(我理解为类似一个临时存放值类型数据的线程栈)
Call Stack:这个是由 .NET CLR 在执行时自动管理的记忆体,每个 Thread 都有自己专属的 Call Stack。每呼叫一次 method,就会使得 Call Stack 上多了一個个 Record Frame;呼叫完成之後,此 Record Frame 会被丢弃(我理解为一个局部变量表,用于存放.locals init(int32 V_0)指令的参数值如:V_0)
.maxstack:代码中变量需要在Call Stack 中占用几个位置
.locals init (int32 V_0,int32 V_1,int32 V_2):定义变量并存入Call Stack中
nop:即No Operation 没有任何操作,我们也不用管它,
ldstr.:即Load String 把字符串加压入Evaluation Stack中
stloc.:把Evaluation Stack中的值弹出赋值到Call Stack中
ldloc.:把Call Stack中指定位置的值取出(copy)存入 Evaluation Stack中 以上两条指令为相互的操作stloc赋值,ldloc取值
call: 调用指定的方法
ret: 即return 标记返回
二、动态生成代码
首先我们需要了解每个动态类型在.net中都是用什么类型来表示的。
程序集:System.Reflection.Emit.AssemblyBuilder(定义并表示动态程序集)
构造函数:System.Reflection.Emit.ConstructorBuilder(定义并表示动态类的构造函数)
自定义属性:System.Reflection.Emit.CustomAttributeBuilder(帮助生成自定义属性 使用构造函数传递的参数来生成类的属性)
枚举:System.Reflection.Emit.EnumBuilder(说明并表示枚举类型)
事件:System.Reflection.Emit.EventBuilder(定义类的事件)
字段:System.Reflection.Emit.FieldBuilder(定义并表示字段。无法继承此类)
局部变量:System.Reflection.Emit.LocalBuilder(表示方法或构造函数内的局部变量)
方法:System.Reflection.Emit.MethodBuilder(定义并表示动态类的方法(或构造函数))
模块:System.Reflection.Emit.ModuleBuilder(定义和表示动态程序集中的模块)
参数:System.Reflection.Emit.ParameterBuilder(创建或关联参数信息 如:方法参数,事件参数等)
属性:System.Reflection.Emit.PropertyBuilder(定义类型的属性 (Property))
类:System.Reflection.Emit.TypeBuilder(在运行时定义并创建类的新实例)
以下介绍Emit生成代码的基本流程:
1.构建程序集
在创建程序集之前,我们先要为它取个名字。
var asmName = new AssemblyName("Test");
AssemblyName位于System.Reflection命名空间下,它代表程序集的名称。
然后我们就可以用上面的名字来创建一个程序集了:
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
AssemblyBuilderAccess.ReflectionOnly:
DefineDynamicAssembly有很多重载,比如上面的例子可以添加第三个参数用于作为生成的程序集要存放到的目录。关于其他重载形式,大家可以查阅MSDN。这里重点说说AssemblyBuilderAccess这个枚举。
它有以下几个值:
AssemblyBuilderAccess.ReflectionOnly:表示动态程序集只能用于反射获取元素据用,不能执行。
AssemblyBuilderAccess.Run:表示动态程序集是用于执行的。
AssemblyBuilderAccess.Save:表示动态程序集会被保存到磁盘上,不能立即执行。
AssemblyBuilderAccess.RunAndSave:表示动态程序集会被保存至磁盘并能立即执行。
2.创建模块
创建程序集后,就需要为程序集添加模块了,我们可以如下定义一个模块:
var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.dll");
如果想把动态生成的程序集保存至磁盘(如本例),定义模块时模块所在文件的名称一定要和保存程序集(后面会提到)时提供的文件名称一样。
3.定义类
有了前面的准备工作,我们开始定义我们的类型:
var typeBldr = mdlBldr.DefineType("Hello",TypeAttributes.Public);
DefineType还可以设置要定义的类的基类,要实现的接口等等。
4.定义类成员(方法,属性等等)
既然有了类,下面我们就为它添加一个SayHello方法吧:
1 var methodBldr = typeBldr.DefineMethod(
2 "SayHello",
3 MethodAttributes.Public,
4 null,//return type
5 null//parameter type
6 );
该方法的原型为public void SayHell();
方法签名已经生成好了,但方法还缺少实现。在生成方法的实现前,必须提及一个很重要的概念:evaluation stack。在.Net下基本所有的操作都是通过入栈出栈完成的。这个栈就是evaluation stack。比如要计算两个数(a,b)的和,首先要将a放入evaluation stack中,然后再将b也放入栈中,最后执行加法时将弹出栈顶的两个元素也就是a和b,相加再将结果推送至栈顶。
Console.WriteLine("Hello,World")可以用Emit这样生成:
1 var il = methodBldr.GetILGenerator();//获取il生成器
2
3 il.Emit(OpCodes.Ldstr,"Hello, World");
4
5 il.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new Type[]{typeof(string)}));
6
7 il.Emit(OpCodes.Ret);
OpCodes枚举定义了所有可能的操作,这里用到了:
ldStr:加载一个字符串到evaluation stack。
Call:调用方法。
Ret:返回,当evaluation stack有值时会返回栈顶值。
完成上面的步骤,一个类型好像就已经完成了。事实上却还没有,最后我们还必须显示的调用CreateType来完成类型的创建。
typeBldr.CreateType();
这样一个完整的类就算完成了。但为了能用reflector查看我们创建的动态程序集,我们选择将这个程序集保存下来。
asmBuilder.Save("Main.dll");
如前面定义模块时所说,这里文件名字必须和模块保存到的文件一致,否则我们前面定义的模块和这个模块的一切就无家可归了。接下来,(如果在定义模块时未指定动态创建的程序要保存到哪个目录)我们就可以到 Debug目录下看看生成的Main.dll了,用Reflector打开可以看到:

三、不包含main的控制台程序
一直以来,应用程序(控制台,winform)都是从Main函数启动的,如果没有Main还能启动吗?答案是可以,下面就用emit来做这样一个控制台程序,完整代码如下:

1 var asmName = new AssemblyName("Test");
2
3 var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
4
5 asmName,
6
7 AssemblyBuilderAccess.RunAndSave);
8
9 var mdlBldr = asmBuilder.DefineDynamicModule("Main", "Main.exe");
10
11 var typeBldr = mdlBldr.DefineType("Hello", TypeAttributes.Public);
12
13 var methodBldr = typeBldr.DefineMethod(
14
15 "SayHello",
16
17 MethodAttributes.Public | MethodAttributes.Static,
18
19 null,//return type
20
21 null//parameter type
22
23 );
24
25 var il = methodBldr.GetILGenerator();//获取il生成器
26
27 il.Emit(OpCodes.Ldstr,"Hello, World");
28
29 il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[]{typeof(string)}));
30
31 il.Emit(OpCodes.Call, typeof(Console).GetMethod("ReadLine"));
32
33 il.Emit(OpCodes.Pop);//读入的值会被推送至evaluation stack,而本方法是没有返回值的,因此,需要将栈上的值抛弃
34
35 il.Emit(OpCodes.Ret);
36
37 var t = typeBldr.CreateType();
38
39 asmBuilder.SetEntryPoint(t.GetMethod("SayHello"));
40
41 asmBuilder.Save("Main.exe");
来源:https://www.cnblogs.com/cjm123/p/8842370.html