基于动态代码生成技术的动态对象工厂
ConstructorInfo ci = Type.GetType(className).GetConstructor(new Type[0]);
Object o1 = ci.Invoke();
Object o2 = Activator.CreateInstance(Type.GetType(className);
上述代码展示了两种基于反射的动态对象创建,但这种方法的效率是比较低下的,特别是在需要大量的动态创建实例的时候。为此我们需要一种更为高效的动态创建实例的方法,动态代码生成就是一种不错的方式。
var myObj = eval(“new “ + className);
C#并没有像eval这样的函数,毕竟编译型语言和脚本语言是不同的,所以要实现类似的功能,就要使用到System.Reflection.Emit名空间下的类来动态的创建出可执行的代码。首先需要认识几个涉及到的类:
...{
public abstract Object CreateObject(Object[] param);
private staticvoid CreateMethod(TypeBuilder tb, Type originalType, Object[] param);
public static Object New(Type type, params Object[] param)
}
抽象方法CreateObject就是用来在子类中重写并创建实例的,静态方法CreateMethod用于实现动态代码生成的过程,静态方法New就是对象暴露的方法,使用者通过这个方法来创建需要的实例,从而模拟new操作符,它的两个参数分别代码要创建的变量的类对象、构造函数的参数,这里使用了关键字params来修释,也就是说它成为一个参数个数可变的函数,可以适应各种参数类型的构造函数。
ModuleBuilder moduleBuilder = dynamicAssembly.DefineDynamicModule("MainModule");
参数AssemblyBuilderAccess.Run表示这个动态创建的程序集只用于执行,而不需要保存。有了程序集和模块之后就需要创建Creator类的子类了,也就是工厂类:
CreateMethod(tb, type, param);
Creator creator = (Creator)Activator.CreateInstance(tb.CreateType());
return creator.CreateObject(param);
这里动态工厂类的类名与要创建的对象的类名相同,名空间前面加上了“__dynamicCreator.”以示区别,参数typeof(Creator)表示这个类要从Creator类继承。然后调用CreateMethod方法来完成动态代码生成,然后调用TypeBuilder的CreateType方法,它会根据之前动态创建的代码生成一个新的类,并在之后可以立即使用,然后我使用Activator.CreateInstance创建出工厂类的实例,之后就可以通过调用这个实例的CreateObject方法来创建出需要的对象了。需要说明的是这里的代码只是一个示例,真正要使用时还需要对创建出的creator对象进行缓存,以后再次创建相同类型的对象时就可以直接使用它的creator对象了。CreateMethod方法是最核心的地方,它需要根据我们指定的类对象和参数找到适当的构造函数,动态为工厂类创建CreateObject方法,在其中调用找到的构造函数,返回构造出的对象。首先得到基类中抽象方法CreateObject的信息:
ConstructorInfo theCi = null;
ParameterInfo[] cpis = null;
foreach(ConstructorInfo ci in cis)
...{
cpis = ci.GetParameters();
if (cpis.Length != param.Length) //参数个数不相符
continue;
theCi = ci;
for (int i = 0; i < cpis.Length; i++)
...{
if (!(param[i] == null || param[i].GetType() == cpis[i].ParameterType || param[i].GetType().IsSubclassOf(cpis[i].ParameterType))) //参数类型不相符
...{
theCi = null;
break;
}
}
if (theCi != null) //如果找到了完全相符的构造函数
break;
}
if (theCi == null)
throw new ArgumentException("错误的参数个数或类型");
现在万事具备,下面就要开始动态生成代码了。要动态的生成可执行代码需要用到ILGenerator类,使用MethodBuilder类的GetILGenerator方法即可以得到这个对象,然后调用它的Emit方法生成中间语言指令:
for (int i = 0; i < param.Length; i++)
...{这里要循环处理传入的每一个参数,以下通过IL来完成取数组元素并压栈的操作:
ilg.Emit(OpCodes.Ldc_I4, i); //把下标压入栈
ilg.Emit(OpCodes.Ldelem_Ref); //以引用的方法从数组中取出需要的内容并放入栈
注意经过Ldelem_Ref指令以后,之前压入栈的两个参数会自动的弹出,而从数组中取得的内容会被放入栈中,所以只要反复的经过上述过程,就可以将传入的参数逐一放入栈中。需要注意的是,这里只处理了引用类型,也就是说如果原来需要的参数是值类弄的,那么参数会在调用函数时被装箱,这里需要还原到原来的值类型,也就是需要一个拆箱操作:
ilg.Emit(OpCodes.Unbox_Any, cpis[i].ParameterType); //拆箱为需要的类型
}
Unbox操作也会自动从栈中取出一个元素,拆箱后再把结果放回栈中,也就是说上述过程不会影响栈中元素的个数。经过上述过程,构造函数的参数就已经准备好并放入栈中了,下面就是调用构造函数了:
...{
public MyClass(int p1, string p2) ...{ }
}
而我们在编译时并没有此类的声明,只有保存了类名称的字符串和构造函数的参数类型,那么可以通过如下方法创建实例:
Type t = Type.GetType(className);
Object o = Creator.New(t, 1, "haha");
用起来还是比较方便的,至少不比反射麻烦。
...{
public A(string s, string s2, string s3, string s4, string s5, string s6, string s7, string s8, string s9) ...{ }
public A(string s, string s2, string s3) ...{ }
public A(string s) ...{ }
public A(int a, int b, int c, int d, int e, int f, int g, int h, int i) ...{ }
public A(int a, int b, int c) ...{ }
public A(int a) ...{ }
}
然后通过四种方式调用这六个构造函数来创建实例:Activator.CreateInstance、ConstructorInfo.Invoke、Creator.New、直接使用new,每种调用都重复1000万次,在Intel PentiumM 1.86G、512M内存、Windows XP SP2、.Net Framewor 2.0上测试结果如下:
调用方式
|
Activator.
CreateInstance
|
ConstructorInfo.
Invoke
|
Creator.New
|
直接使用new
|
|
引用类型
|
1个参数
|
59281.25
|
18843.75
|
2296.875
|
140.625
|
3个参数
|
72031.25
|
24000
|
2453.125
|
171.875
|
|
9个参数
|
102843.75
|
39218.75
|
3187.5
|
156.25
|
|
值类型
|
1个参数
|
60468.75
|
19921.875
|
2375
|
109.375
|
3个参数
|
73953.125
|
26390.625
|
2796.875
|
109.375
|
|
9个参数
|
110656.25
|
46765.625
|
4453.125
|
109.375
|
可见,直接使用new还是最快的,动态代码生成的方法还是要比直接使用new慢了15-40倍,但比使用Activator的方法快20倍左右,比Invoke的方法快10倍左右,因此在不能直接使用new的时候,动态代码生成的方法还是非常实用的。
...{
private static AssemblyBuilder dynamicAssembly = null;
private static ModuleBuilder moduleBuilder = null;
private static Dictionary<Type, Creator> creatorList = new Dictionary<Type, Creator>();
private static ModuleBuilder GetDynamicModule()
...{
if (dynamicAssembly == null)
...{
dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run);
moduleBuilder = dynamicAssembly.DefineDynamicModule("MainModule");
}
return moduleBuilder;
}
private static void CreateMethod(TypeBuilder tb, Type originalType, Object[] param)
...{
MethodInfo mi = typeof(Creator).GetMethod("CreateObject");
MethodBuilder mb = tb.DefineMethod("CreateObject", mi.Attributes & ~MethodAttributes.Abstract, mi.CallingConvention, mi.ReturnType, new Type[] ...{ typeof(Object[]) });
ConstructorInfo[] cis = originalType.GetConstructors();
ConstructorInfo theCi = null;
ParameterInfo[] cpis = null;
foreach(ConstructorInfo ci in cis)
...{
cpis = ci.GetParameters();
if (cpis.Length != param.Length)
continue;
theCi = ci;
for (int i = 0; i < cpis.Length; i++)
...{
if (!(param[i] == null || param[i].GetType() == cpis[i].ParameterType || param[i].GetType().IsSubclassOf(cpis[i].ParameterType)))
...{
theCi = null;
break;
}
}
if (theCi != null)
break;
}
if (theCi == null)
throw new ArgumentException("错误的参数个数或类型");
ILGenerator ilg = mb.GetILGenerator();
for (int i = 0; i < param.Length; i++)
...{
ilg.Emit(OpCodes.Ldarg_1);
ilg.Emit(OpCodes.Ldc_I4, i);
ilg.Emit(OpCodes.Ldelem_Ref);
if (cpis[i].ParameterType.IsValueType)
ilg.Emit(OpCodes.Unbox_Any, cpis[i].ParameterType);
}
ilg.Emit(OpCodes.Newobj, theCi);
ilg.Emit(OpCodes.Ret);
tb.DefineMethodOverride(mb, mi); // 定义方法重载
}
private static Creator GetCreator(Type type, Object[] param)
...{
if(!creatorList.ContainsKey(type))
...{
ModuleBuilder module = GetDynamicModule();
TypeBuilder tb = module.DefineType("__dynamicCreator." + type.FullName, TypeAttributes.Public | TypeAttributes.Class, typeof(Creator));
CreateMethod(tb, type, param);
creatorList.Add(type, (Creator)Activator.CreateInstance(tb.CreateType()));
}
return creatorList[type];
}
public abstract Object CreateObject(Object[] param);
public static Object New(Type type, params Object[] param)
...{
Creator creator = GetCreator(type, param);
return creator.CreateObject(param);
}
}
转载于:https://www.cnblogs.com/springMVC/archive/2007/02/23/2204713.html
基于动态代码生成技术的动态对象工厂相关推荐
- 基于三维GIS技术的动态LOD渲染方法的实验及验证
针对透视投影下现有矢量地图动态LOD渲染方法存在的不足,本文从渲染的客观需求出发使用梯形格网统一了简化与剖分的过程,并研究了透视投影下梯形格网的简化算法.之后,分析了地图简化的影响因素,最终,针对GP ...
- 动态网页技术JSP、ASP、PHP
动态网页技术 所谓"动态",并不是指那几个放在网页上的GIF图片,动态网页技术有以下几个特点. 1,"交互性"即网页会根据用户的要求和选择而动态改变和响应,将浏 ...
- Java 静态代理、Java动态代理、CGLIB动态代理
为什么80%的码农都做不了架构师?>>> Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理.为啥要这样呢, 是因为使用代理有 ...
- 基于Java Web技术的动车购票系统
毕 业 设 计 中文题目 基于Java Web技术的动车购票系统 英文题目 Train ticket system based on Web Java Technology 毕业设计诚信声明书 本人郑 ...
- android 系统gpu 调试_基于Android系统的GPU动态调频方案 | Imagination中文技术社区
针对移动终端上GPU的高功耗问题,提出一种基于Android系统的GPU动态调频方案.方案根据各种应用对GPU的性能需求,引入了GPU的频率一性能模型,包括选择工作频率和测量相对性能的方法.动态调频算 ...
- 基于三维GIS技术的矢量地图动态LOD渲染方法研究现状
"地图是人类文化的杰作,它融科学.艺术于一体,作为描述.研究人类生存环境的一种信息载体是人类生产与生活中不可缺少的一种工具."这是陈述彭院士为<中国地图学年鉴>作序的开 ...
- android 动态更新配置文件,基于DX的Android动态更新技术
原标题:基于DX的Android动态更新技术 转自:文/Mob开发者平台 技术副总监 余勋杰 DX简介 安卓程序的主要代码是java 代码,不过由于安卓系统不直接使用sun的jvm,所以从javac编 ...
- 【得物技术】基于配置的通用化动态报表平台设计与使用
引言 不会写代码的业务方不是好研发!报表作为一种供业务人员随时随地掌握业务情况必不可少的工具,在开发阶段往往存在种类繁杂.前后端开发繁琐等问题,开发一张报表可能需要几个工作日.如何提高报表的制作效率, ...
- php动态网页学生作品,基于php动态网页技术的办公网站的设计
建立一个基于php动态网页技术的网络办公平台,主要用于信息的分享与办公的直接化方便化.用户可以用此网站及时的不限地点的对自己工作任务进行了解,工作资料的下载,工作问题进行讨论. 1.课题任务 (1) ...
最新文章
- 安装npm_前端开发:node.js的node包管理器npm安装以及使用
- Linux C 数据结构---单向链表
- ViewGroup之getScrollX()
- 如何快速的了解java的I/O流,由浅到深
- 为什么天才容易患阅读障碍症_患了慢阻肺为什么容易日渐消瘦?
- python斐波那契递归_Python递归斐波那契示例
- 小甲鱼python课后题答案_小甲鱼python课后习题总结
- 动态URL、静态URl、伪静态URL
- 控制工程实践(2)——拉普拉斯变换及传递函数(之拉氏变换)
- 江西银行服务器怎么选择硬件配置
- View 5应用之二:瘦客户机上的虚拟桌面
- c语言生日快乐音乐程序,89S51演奏生日快乐的歌曲c程序
- 【水晶报表】中如何打印条形码?
- activiti+app+mysql_SpringBoot Activiti6系列教程(一)-activiti-app部署
- 2022年湖南省临床执业医师考试第四单元随机模拟题
- inprivate浏览是什么意思_IE8的InPrivate浏览功能
- 第69天-内网安全-域横向 CobaltStrikeSPNRDP
- 四类九种移位寄存器总结(循环(左、右、双向)移位寄存器、逻辑和算术移位寄存器、串并转换移位寄存器、线性反馈移位寄存器LFSR|verilog代码|Testbench|仿真结果)
- java:Cassandra入门与实战——上
- python数据分析pandas_利用Python进行数据分析笔记-pandas建模(Patsy篇)
热门文章
- MATLAB 中怎么求图像在水平方向和垂直方向的像素和,用图表示
- 【解决方案】Basemap安装出现的错误(Python)
- 实现对文本的简单one-hot编码
- Java基础点:集合
- centos7部署两个mysql_centos7 安装mysql5.7主从复制主写分离
- 2.7 HBase架构深入剖析
- (二)linux内核准备及编译
- script 有哪个属性可以让它不立即执行 defer,async
- 【BZOJ-3196】二逼平衡树 线段树 + Splay (线段树套平衡树)
- 常微分方程I ODE的例子1 弹簧的振动、RLC电路与单摆