开发者社区> kenshincui> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

反射发出--Emit

简介:
+关注继续查看

"反射"和"反射发出(Emit)"的关系

相信反射大家都不陌生,我也曾写过关于反射的文章,大家有兴趣可以看看,但是今天要说的不是"反射"而是"反射发出(Emit)"。我们知道反射的主要功能是获得对象的信息、调用对象的方法等;而反射发出的主要功能是动态的创建对象。那么是不是二者就没有关系呢?事实上二者不经有关系而且关系十分密切。从命名空间我们就可以看出来,反射处于System.Reflection 命名空间下,而反射发出Emit处于System.Reflection.Emit 命名空间下,它的功能虽然主要是动态创建对象,但是很多反射能够完成的工作它也可以完成而且速度更快(实现当然较反射而言要麻烦一些)。也就是说反射主要用到对象已经存在的情况下,而反射发出主要用到对象并不存在等情况下(而利用代码动态的构建对象)。

"反射发出(Emit)"动态构建对象的原理

我们既然知道了Emit可以动态创建对象,那么Emit是如何做到呢?这就必须要提到MSIL,它是类似于java虚拟机的一种无关于CPU的中间语言,也就是说不管你是用什么语言只要最终生成IL,那么.Net就可以执行(这也是.Net上为什么能够运行C#、VB、F#等多种语言的原因)。利用Emit之所以能够动态构建对象,也是因为它可以直接生成IL,这样一来我们当然也就可以动态创建对象了,而且速度几乎接近于直接运行已有代码。

"反射发出(Emit)"的用途

Emit最典型的应用就是:增强反射性能、动态代理(AOP)、ORM实现等。

"反射发出(Emit)"的实现

使用Emit的步骤几乎是固定的,网上很多文章都列举的很清楚,这里我们就先看代码吧。假设现在我准备构建一个这样的对象:

 

复制代码

1 using System;
2  using System.Collections.Generic;
3  using System.Linq;
4  using System.Text;
5 namespace MyEmit
6 {
7 public class MyClass
8 {
9 private double a = 0;
10 private double b = 0;
11 public MyClass()
12 {
13 }
14 public MyClass(double a,double b)
15 {
16 this.a = a;
17 this.b = b;
18 }
19
20 public double Sum()
21 {
22 return a + b;
23 }
24 public double A
25 {
26 get
27 {
28 return a;
29 }
30 set
31 {
32 a = value;
33 }
34 }
35 }
36 }
37
复制代码

 

 

上面的代码我们直接书写然后在vs中编译几乎是再简单不过的事情了,可是它包括了成员变量、构造函数、属性、方法等多个对象常用元素,最为一个例子应该说是比较有代表性的了。下面就来看看我们如何使用代码动态构建来代替手工书写编译的过程吧:

 

复制代码

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Reflection;
6 using System.Reflection.Emit;
7 namespace Emit_ConsoleApplication
8 {
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 //构建程序集
14 AssemblyName aName = new AssemblyName("MyEmit");
15 AppDomain aDomain = AppDomain.CurrentDomain;//应用程序域,这里设为当前域
16 AssemblyBuilder aBuidler = aDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
17 //定义模块
18 ModuleBuilder mBuidler = aBuidler.DefineDynamicModule("MyModule");
19 //创建类型(其实就是一个类)
20 TypeBuilder tBuidler = mBuidler.DefineType("MyEmit.MyClass", TypeAttributes.Public | TypeAttributes.Class);
21 //--实现类--//
22 //定义两个私有字段
23 FieldBuilder fb_a = tBuidler.DefineField("a", typeof(double), FieldAttributes.Private);
24 FieldBuilder fb_b = tBuidler.DefineField("b", typeof(double), FieldAttributes.Private);
25 //为私有变量赋值
26 fb_a.SetConstant((double)0);
27 fb_b.SetConstant((double)0);
28 //定义构造函数
29 ConstructorBuilder ctstBuilderWithoutParameters = tBuidler.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, null);//无参数构造函数
30 ILGenerator ctstWithoutParametersGenerator = ctstBuilderWithoutParameters.GetILGenerator();
31 ctstWithoutParametersGenerator.Emit(OpCodes.Ret);
32 ConstructorBuilder ctstBuilder = tBuidler.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeof(double), typeof(double) });
33 ILGenerator ctstGenerator=ctstBuilder.GetILGenerator();
34 ctstGenerator.Emit(OpCodes.Ldarg_0);
35 ctstGenerator.Emit(OpCodes.Ldarg_1);
36 ctstGenerator.Emit(OpCodes.Stfld, fb_a);//给字段赋值
37 ctstGenerator.Emit(OpCodes.Ldarg_0);
38 ctstGenerator.Emit(OpCodes.Ldarg_2);
39 ctstGenerator.Emit(OpCodes.Stfld, fb_b);
40
41 ctstGenerator.Emit(OpCodes.Ret);
42 //定义属性
43 PropertyBuilder pro_A = tBuidler.DefineProperty("A", PropertyAttributes.None, typeof(double), null);
44 MethodBuilder mBuidlerGetA = tBuidler.DefineMethod("get", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(double), Type.EmptyTypes);//属性的get方法
45 ILGenerator getAGenerator = mBuidlerGetA.GetILGenerator();
46 getAGenerator.Emit(OpCodes.Ldarg_0);
47 getAGenerator.Emit(OpCodes.Ldfld,fb_a);
48 getAGenerator.Emit(OpCodes.Ret);
49 MethodBuilder mBuidlerSetA = tBuidler.DefineMethod("set", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new Type[]{typeof(double)});
50 ILGenerator setAGenerator = mBuidlerSetA.GetILGenerator();
51 setAGenerator.Emit(OpCodes.Ldarg_0);
52 setAGenerator.Emit(OpCodes.Ldarg_1);
53 setAGenerator.Emit(OpCodes.Ldfld, fb_a);
54 setAGenerator.Emit(OpCodes.Ret);
55 //定义方法
56 MethodBuilder mtdBuidler = tBuidler.DefineMethod("Sum",MethodAttributes.Public,typeof(double),null);
57 ILGenerator mtdGenerator = mtdBuidler.GetILGenerator();
58 mtdGenerator.Emit(OpCodes.Ldarg_0);
59 mtdGenerator.Emit(OpCodes.Ldfld,fb_a);
60 mtdGenerator.Emit(OpCodes.Ldarg_0);
61 mtdGenerator.Emit(OpCodes.Ldfld,fb_b);
62 mtdGenerator.Emit(OpCodes.Add);
63 mtdGenerator.Emit(OpCodes.Ret);
64
65 //创建引用、调用方法
66 Type MyClass = tBuidler.CreateType();
67 object myInstance = Activator.CreateInstance(MyClass,new object[]{1.1,2.2});//实例化
68 //object result = MyClass.InvokeMember("Add", BindingFlags.InvokeMethod, null, myInstance, new object[]{1.2,2.3});
69 object result = MyClass.GetMethod("Sum").Invoke(myInstance,null);
70 Console.WriteLine("this result is {0}", result.ToString());
71 }
72 }
73 }
74
复制代码

 

 

上面的注释已经说明了使用Emit的常用步骤,一步步书写就可以了,但是关键就是方法实现部分的代码不是太容易,但我们上面已经说过使用Emit的过程可以看成是构建IL的过程,如果我们能够看到我们想要构建的对象的IL代码再用Emit来书写就容易多了。这里就向大家推荐一个工具—reflector,我们可以先将上面MyEmit.MyClass这个类编译成dll或exe,然后使用reflector查看器相应的IL代码,然后在书写Emit的时候(主要是方法体的实现)对照IL来书写就容易多了。例如上面Sum方法的IL代码:

 

复制代码

1 .method public hidebysig instance float64 Sum() cil managed
2 {
3 .maxstack 2
4 .locals init (
5 [0] float64 CS$1$0000)
6 L_0000: nop
7 L_0001: ldarg.0
8 L_0002: ldfld float64 MyEmit.MyClass::a
9 L_0007: ldarg.0
10 L_0008: ldfld float64 MyEmit.MyClass::b
11 L_000d: add
12 L_000e: stloc.0
13 L_000f: br.s L_0011
14 L_0011: ldloc.0
15 L_0012: ret
16 }
复制代码

 

 

当然,reflector反编译过的IL可以帮助我们,但是只能够作为参考,中间需要增减相关代码,如果想要彻底的了解Emit中IL的书写还需要多加学习。另外,上面我们的程序集、模块、构造函数、属性、方法等的创建都是使用Emit来实现的,但是最后对于类的实例化和方法的调用却是典型的反射应用,当然这些我们仍然可以使用Emit来做到,而且网上也有很多的介绍,如果今后有时间我会再和大家一块学习的,今天就先到这里吧。

知识共享许可协议 本作品采用知识共享署名 2.5 中国大陆许可协议进行许可,欢迎转载,演绎或用于商业目的。但转载请注明来自崔江涛(KenshinCui),并包含相关链接。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
小程序--event对象
小程序--event对象
0 0
用Emit技术替代反射
System.Reflection.Emit命名空间类可用于动态发出Microsoft中间语言(MSIL)代码,以便生成的代码可以直接执行。反射也用于获取有关类及其成员的信息。换句话说,反射是一种技术,允许您检查描述类型及其成员的元数据,你可能以编程方式访问过组件对象模型类型库, .NET中的反射非常相似,但功能强大且易于使用。
727 0
事件和方法的区别
事件:指的是一个类有可能会引发的一个调用,任何类都可以向一个有事件的类注册这个事件的监听,那么当事件引发时,类就会受到事件通知,从而响应。 方法:指的是一个类的一些操作,比如一个Car类他有一个Move的移动方法,表示这个车子可以移动的操作(Operation)。
462 0
serialPort 接收事件不触发及接收长数据
这里关注两个问题: 1、DataReceived 不能触发问题 2、接收大于8的数据分段发回的问题 public static bool OpenDeviceCOM(string portName) ...
956 0
.net中用Action等委托向外传递参数
原文:.net中用Action等委托向外传递参数      一般我们可以使用ref,out达到向外传递参数目的。 Action是一个特殊的委托,除了常规应用。我们还可以用它来实现简单地向外传递参数。直接看下面的UnitTest代码: 1: [TestMethod] ...
452 0
自定义CancelEventArgs类,封装事件参数信息,实现e.Cancle=true取消机制。
参考文章:http://blog.csdn.net/lastinglate/article/details/5753113 如果您正在设计可取消的事件,请使用CancelEventArgs(而非是EventArgs)作为事件数据对象e的基类。
486 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载