基于动态代码生成技术的动态对象工厂
来源:互联网 发布:显示不安全的网络 编辑:程序博客网 时间:2024/06/05 17:10
C#中所有的引用类型的实例都需要在运行时动态创建,创建对象实例最常见的办法就是使用new操作符,使用new操作符就需要在编译器明确的知道要创建的对象的类型,如果在编译器并不能明确,就需要用到反射技术,例如:
String className = "MyNamesapce.MyClass";
ConstructorInfo ci = Type.GetType(className).GetConstructor(new Type[0]);
Object o1 = ci.Invoke();
Object o2 = Activator.CreateInstance(Type.GetType(className);
ConstructorInfo ci = Type.GetType(className).GetConstructor(new Type[0]);
Object o1 = ci.Invoke();
Object o2 = Activator.CreateInstance(Type.GetType(className);
之所以不能直接使用new,就是因为new后面的类型参数在编译器是不知道的,那么就需要在运行的时候动态的创建出与new相配合的代码。这类似于在Javascript中使用eval函数:
var className = “MyClass”;
var myObj = eval(“new “ + className);
var myObj = eval(“new “ + className);
System.Reflection.Emit.AssemblyBuilder:用来动态创建程序集
System.Reflection.Emit.ModuleBuilder:用来动态创建模块
System.Reflection.Emit.TypeBuilder:用来动态创建类型
System.Reflection.Emit.MethodBuilder:用来动态创建方法
这里我的设计思想是,首先创建一个抽象基类(Creator类),它声明了一个用于动态创建需要的对象实例的抽象方法,在运行时根据需要动态的创建出这个抽象类的子类,并动态实现这个抽象方法,编写出用于创建对象的代码。在基类中提供一些静态方法来实现子类的创建过程,并对外提供可调用的方法。这是抽象工厂模式的一种实现。基类的声明如下:
public abstract class Creator
...{
public abstract Object CreateObject(Object[] param);
private staticvoid CreateMethod(TypeBuilder tb, Type originalType, Object[] param);
public static Object New(Type type, params Object[] param)
}
...{
public abstract Object CreateObject(Object[] param);
private staticvoid CreateMethod(TypeBuilder tb, Type originalType, Object[] param);
public static Object New(Type type, params Object[] param)
}
New方法里面首先要动态的创建程序集和模块:
AssemblyBuilder dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = dynamicAssembly.DefineDynamicModule("MainModule");
ModuleBuilder moduleBuilder = dynamicAssembly.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType("__dynamicCreator." + type.FullName, TypeAttributes.Public | TypeAttributes.Class, typeof(Creator));
CreateMethod(tb, type, param);
Creator creator = (Creator)Activator.CreateInstance(tb.CreateType());
return creator.CreateObject(param);
CreateMethod(tb, type, param);
Creator creator = (Creator)Activator.CreateInstance(tb.CreateType());
return creator.CreateObject(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("错误的参数个数或类型");
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++)
...{这里要循环处理传入的每一个参数,以下通过IL来完成取数组元素并压栈的操作:
for (int i = 0; i < param.Length; i++)
...{这里要循环处理传入的每一个参数,以下通过IL来完成取数组元素并压栈的操作:
ilg.Emit(OpCodes.Ldarg_1); //把参数数组放入栈
ilg.Emit(OpCodes.Ldc_I4, i); //把下标压入栈
ilg.Emit(OpCodes.Ldelem_Ref); //以引用的方法从数组中取出需要的内容并放入栈
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.Unbox_Any, cpis[i].ParameterType); //拆箱为需要的类型
}
ilg.Emit(OpCodes.Newobj, theCi);
指令Newobj相当于关键字new,用于调用构函数,参数theCi就是要创建的对象的构造函数信息。经过这个过程原栈中所压的构造函数参数都被弹出,然后把创建后的对象放回到栈中。下面只需要通过Ret指令就可以把栈中唯一的元素作为函数的返回值返回给调用者:
ilg.Emit(OpCodes.Ret);
到这里所需要的代码就已经动态生成完毕,以后再通过Creator类的子类调用CreateObject方法时,执行的就是上述动态生成的代码了。但只有这些还不够,Creator子类的虚方法表还没有更新,需要调用TypeBuilder类的DefineMethodOverride方法明确的指出用子类中的方法覆盖基类中的虚方法:
tb.DefineMethodOverride(mb, mi); // 定义方法重载
这段代码虽然比直接使用反射技术要复杂很多,而且里面也多处使用了反射技术,但它只有在第一次使用时被调用到,之后就只调用动态生成的代码,因此对性能影响是可以忽略的。
下面就是要使用上述代码了。假如我们有一个类如下:
public class MyClass
...{
public MyClass(int p1, string p2) ...{ }
}
...{
public MyClass(int p1, string p2) ...{ }
}
String className = "MyClass";
Type t = Type.GetType(className);
Object o = Creator.New(t, 1, "haha");
Type t = Type.GetType(className);
Object o = Creator.New(t, 1, "haha");
为了进一步研究这种方法相对于反射方法的优势,我进行了一组实验。首先构造一个类,它有六个构造函数,分别用于测试一个、三个、九个值类型、引用类型参数时的性能:
public class A
...{
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) ...{ }
}
...{
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) ...{ }
}
各种调用类型重复1000万次所需时间(毫秒)
调用方式
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的时候,动态代码生成的方法还是非常实用的。
附完整的源代码如下。此代码仍有一些问题,如当一个类有多个构造函数时,它只能缓存一个构造函数,第二次如果调用另一个则会出错,可以适当的改进来解决此问题。
public abstract class Creator
...{
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);
}
}
...{
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);
}
}
- 基于动态代码生成技术的动态对象工厂
- 基于动态代码生成技术的动态对象工厂
- 基于动态代码生成技术的动态对象工厂
- 基于动态代码生成技术的动态对象工厂
- 对《基于动态代码生成技术的动态对象工厂》一文的补充
- 对《基于动态代码生成技术的动态对象工厂》一文的补充
- 借助动态代码生成技术在基于Webkit引擎的HTML5网页JS内调用易语言函数
- C#反射动态生成对象的关键代码
- 动态生成技术 [原]
- 基于Java模板技术动态生成Word文档
- 基于Delphi的Excel动态报表技术
- 基于Delphi的Excel动态报表技术
- 动态生成对象
- JAVA动态生成对象
- 动态生成DIV 对象
- 动态工厂模式代码实例
- 一个基于组件的动态对象系统
- 一个基于组件的动态对象系统
- 转贴:内存泄漏检查
- VB.net 2005实现TCP网络通信例程(.net VS2005)源码
- 成为有钱人的25种方法
- Windows XP SP2 IIS5.1安装文件
- 菜鸟也能当PS高手!PhotoWorks为你的图片添加漂亮的边框(教程+下载)
- 基于动态代码生成技术的动态对象工厂
- 观影片《当幸福来敲门》有感
- 2007年第7周最热网友收藏
- 正式开博——以love的名义(delphi入门笔记)
- JavaScript的方法和技巧
- Frances Allen荣获2006年图灵奖
- 【 让你的磁盘背景变成图片 】
- 围棋的意义
- Gentoo Overlay