基于动态代码生成技术的动态对象工厂
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);
}
}