基于CodeGenerator的Emit代码生成辅助类源码及演示
本文介绍一组NBearV4中的基于Emit动态生成代码的辅助类,部分概念在本人的blog之前的文章中或多或少都有介绍,这里包含最新的更新及演示、测试。主要是两个类:CodeGenerator和DynamicMethodFactory。前者提供了一种经过封装的,简化Emit方法(包括Emit DynamicMethod,Constructor,Method,get、set Method of Property)的方案;后者基于前者,实现了一种访问指定类(可以是第三方程序集的internal类)的方法或成员变量,实例化第三方程序集中的internal类型,高性能的以非泛型语法访问泛型方法的机制(通过DynamicMethod和Delegate实现)。
下载源码:NBear.Common.zip
介绍
CodeGenerator
该类很多地方参照了.NET 3.0的System.Runtime.Serialization.dll中的同名internal类,他封装了Emit中的各种Emit层面的常用操作逻辑,包括Ld各种value、成员变量,if-else,case switch,loop等分支控制等,扩展的版本使用DesignByContract对所有的输入参数进行了检查,并扩展了对Emit Constructor,Method,get、set Method of Property的支持。
关于Emit DynamicMethod的示例,大家可以参见稍后介绍的DynamicMethodFactory类,这里先给出一个使用该类Emit一个类,并实现一个接口的示例代码,该示例代码为包含于源码的CodeGenerator.cs文件末尾的UnitTest代码:
以上代码Emit了一个TestImpl类,它实现了ITest接口,包含一个默认构造函数和一个Wow方法,注意,构造函数和方法都是通过CodeGenerator Emit的,这里的逻辑比较简单,但应该已经能看到相对于ilGen.Emit(OpCodes.XXX, YYY)这样的语法的简化,如果实现逻辑复杂,对整个Emit过程的简化就更明显。
DynamicMethodFactory
该类的主要功能包括:实例化第三方程序集中的internal类型(DynamicMethodFactory.CreateInstance()方法),为指定类型(可以是第三方程序集中的internal类型)的泛型或非泛型方法、属性、字段的读写生成非强类型的Delegate(通过DynamicMethod实现,不使用反射,性能接近直接访问)。
下面先给出一个该类中为一个Method创建一个DynamicMethod,并返回其Delegate的示例,DynamicMethod是使用前面介绍的CodeGenerator实现的:
LoadParameters和CastValueToObject的代码
代码是不是相对比较简单呢(当然是相对于自己写所有的Emit来讲的),注意这里的LoadParameter方法的实现您可以发现,为方法生成调用Delegate是完美支持输入输出参数的。
下面给出DynamicMethodFactory类的UnitTest代码,演示了对方法、字段和属性的生成Delegate和基于Delegate的读写,且包括对输入输出参数的使用:
下载源码:NBear.Common.zip
介绍
CodeGenerator
该类很多地方参照了.NET 3.0的System.Runtime.Serialization.dll中的同名internal类,他封装了Emit中的各种Emit层面的常用操作逻辑,包括Ld各种value、成员变量,if-else,case switch,loop等分支控制等,扩展的版本使用DesignByContract对所有的输入参数进行了检查,并扩展了对Emit Constructor,Method,get、set Method of Property的支持。
关于Emit DynamicMethod的示例,大家可以参见稍后介绍的DynamicMethodFactory类,这里先给出一个使用该类Emit一个类,并实现一个接口的示例代码,该示例代码为包含于源码的CodeGenerator.cs文件末尾的UnitTest代码:
1
namespace CodeGeneratorUnitTest
2
{
3
public interface ITest
4
{
5
string Wow(string str);
6
}
7
8
public class UnitTest
9
{
10
public static void TestEmitInterface()
11
{
12
AssemblyName assName = new AssemblyName("TestEmitInterface");
13
AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
14
ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name);
15
TypeBuilder typeBuilder = modBuilder.DefineType("TestEmitInterface.TestImpl", TypeAttributes.Public);
16
typeBuilder.AddInterfaceImplementation(typeof(ITest));
17
18
CodeGenerator ctor = new CodeGenerator(typeBuilder, "ctor", MethodAttributes.Public, CallingConventions.Standard, null, Type.EmptyTypes);
19
ctor.Ldarg(0);
20
ctor.Call(typeof(object).GetConstructor(Type.EmptyTypes));
21
ctor.Ret();
22
23
MethodInfo mi = typeof(ITest).GetMethod("Wow");
24
25
CodeGenerator wow = new CodeGenerator(typeBuilder, mi.Name, mi.Attributes & (~MethodAttributes.Abstract) | MethodAttributes.Public, mi.CallingConvention, mi.ReturnType, new Type[] { typeof(string) });
26
wow.Ldarg(1);
27
wow.Ret();
28
29
typeBuilder.DefineMethodOverride(wow.CurrentMethod, mi);
30
31
Type testImplType = typeBuilder.CreateType();
32
ITest test = (ITest)Activator.CreateInstance(testImplType);
33
Check.Assert(test.Wow("hello") == "hello");
34
}
35
}
36
}
namespace CodeGeneratorUnitTest2
{3
public interface ITest4
{5
string Wow(string str);6
}7

8
public class UnitTest9
{10
public static void TestEmitInterface()11
{12
AssemblyName assName = new AssemblyName("TestEmitInterface");13
AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);14
ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name);15
TypeBuilder typeBuilder = modBuilder.DefineType("TestEmitInterface.TestImpl", TypeAttributes.Public);16
typeBuilder.AddInterfaceImplementation(typeof(ITest));17

18
CodeGenerator ctor = new CodeGenerator(typeBuilder, "ctor", MethodAttributes.Public, CallingConventions.Standard, null, Type.EmptyTypes);19
ctor.Ldarg(0);20
ctor.Call(typeof(object).GetConstructor(Type.EmptyTypes));21
ctor.Ret();22

23
MethodInfo mi = typeof(ITest).GetMethod("Wow");24

25
CodeGenerator wow = new CodeGenerator(typeBuilder, mi.Name, mi.Attributes & (~MethodAttributes.Abstract) | MethodAttributes.Public, mi.CallingConvention, mi.ReturnType, new Type[] { typeof(string) });26
wow.Ldarg(1);27
wow.Ret();28

29
typeBuilder.DefineMethodOverride(wow.CurrentMethod, mi);30

31
Type testImplType = typeBuilder.CreateType();32
ITest test = (ITest)Activator.CreateInstance(testImplType);33
Check.Assert(test.Wow("hello") == "hello");34
}35
}36
}以上代码Emit了一个TestImpl类,它实现了ITest接口,包含一个默认构造函数和一个Wow方法,注意,构造函数和方法都是通过CodeGenerator Emit的,这里的逻辑比较简单,但应该已经能看到相对于ilGen.Emit(OpCodes.XXX, YYY)这样的语法的简化,如果实现逻辑复杂,对整个Emit过程的简化就更明显。
DynamicMethodFactory
该类的主要功能包括:实例化第三方程序集中的internal类型(DynamicMethodFactory.CreateInstance()方法),为指定类型(可以是第三方程序集中的internal类型)的泛型或非泛型方法、属性、字段的读写生成非强类型的Delegate(通过DynamicMethod实现,不使用反射,性能接近直接访问)。
下面先给出一个该类中为一个Method创建一个DynamicMethod,并返回其Delegate的示例,DynamicMethod是使用前面介绍的CodeGenerator实现的:
1
protected static DynamicMethodProxyHandler DoGetMethodDelegate(
2
Module targetModule,
3
MethodInfo genericMethodInfo,
4
params Type[] genericParameterTypes)
5
{
6
Check preconditions
16
17
//Create a dynamic method proxy delegate used to call the specified methodinfo
18
CodeGenerator gen = new CodeGenerator(targetModule);
19
gen.BeginMethod("dm" + Guid.NewGuid().ToString("N"), typeof(DynamicMethodProxyHandler));
20
MethodInfo makeGenericMethodInfo = MakeMethodGeneric(genericMethodInfo, genericParameterTypes);
21
gen.Ldarg(0);
22
LoadParameters(gen, makeGenericMethodInfo.GetParameters(), false);
23
gen.Call(makeGenericMethodInfo);
24
CastValueToObject(gen, makeGenericMethodInfo.ReturnType);
25
26
return (DynamicMethodProxyHandler)gen.EndMethod();
27
}
protected static DynamicMethodProxyHandler DoGetMethodDelegate(2
Module targetModule,3
MethodInfo genericMethodInfo,4
params Type[] genericParameterTypes)5
{6
Check preconditions16

17
//Create a dynamic method proxy delegate used to call the specified methodinfo18
CodeGenerator gen = new CodeGenerator(targetModule);19
gen.BeginMethod("dm" + Guid.NewGuid().ToString("N"), typeof(DynamicMethodProxyHandler));20
MethodInfo makeGenericMethodInfo = MakeMethodGeneric(genericMethodInfo, genericParameterTypes);21
gen.Ldarg(0);22
LoadParameters(gen, makeGenericMethodInfo.GetParameters(), false);23
gen.Call(makeGenericMethodInfo);24
CastValueToObject(gen, makeGenericMethodInfo.ReturnType);25

26
return (DynamicMethodProxyHandler)gen.EndMethod();27
}代码是不是相对比较简单呢(当然是相对于自己写所有的Emit来讲的),注意这里的LoadParameter方法的实现您可以发现,为方法生成调用Delegate是完美支持输入输出参数的。
下面给出DynamicMethodFactory类的UnitTest代码,演示了对方法、字段和属性的生成Delegate和基于Delegate的读写,且包括对输入输出参数的使用:
1
namespace DynamicMethodFactoryUnitTest
2
{
3
public class TestClass
4
{
5
public static void StaticReturnVoidMethod()
6
{
7
}
8
9
public static int StaticReturnIntMethod(string str, int i, ref int refInt, ref string refStr)
10
{
11
Check.Assert(str == "str");
12
Check.Assert(i == 1);
13
Check.Assert(refInt == 3);
14
Check.Assert(refStr == "instr");
15
16
int ret = i + refInt;
17
refInt = i + 1;
18
refStr = "ref" + str;
19
20
Check.Assert(refInt == 2);
21
Check.Assert(ret == 4);
22
Check.Assert(refStr == "refstr");
23
24
return ret;
25
}
26
27
public static int StaticIntField;
28
29
public static int StaticIntProperty
30
{
31
get
32
{
33
return StaticIntField;
34
}
35
set
36
{
37
StaticIntField = value;
38
}
39
}
40
41
public void NonStaticReturnVoidMethod()
42
{
43
}
44
45
public int NonStaticReturnIntMethod(string str, int i, out int outInt, out string outStr)
46
{
47
outInt = i + 1;
48
Check.Assert(outInt == 2);
49
outStr = "out" + str;
50
Check.Assert(outStr == "outstr");
51
return i + 2;
52
}
53
54
public int NonStaticIntField;
55
56
public int NonStaticIntProperty
57
{
58
get
59
{
60
return NonStaticIntField;
61
}
62
set
63
{
64
NonStaticIntField = value;
65
}
66
}
67
}
68
69
public class UnitTest
70
{
71
private static DynamicMethodFactory fac = new DynamicMethodFactory();
72
73
public static void TestStaticMethod()
74
{
75
StaticDynamicMethodProxyHandler handler = fac.GetStaticMethodDelegate(typeof(TestClass).GetMethod("StaticReturnVoidMethod"));
76
handler(null);
77
78
object[] inputParams = new object[] { "str", 1, 3, "instr" };
79
handler = fac.GetStaticMethodDelegate(typeof(TestClass).GetMethod("StaticReturnIntMethod"));
80
object ret = handler(inputParams);
81
Check.Assert(((int)inputParams[2]) == 2);
82
Check.Assert(((string)inputParams[3]) == "refstr");
83
Check.Assert(((int)ret) == 4);
84
}
85
86
public static void TestStaticField()
87
{
88
TestClass.StaticIntField = -1;
89
FieldInfo field = typeof(TestClass).GetField("StaticIntField"); ;
90
StaticDynamicMethodProxyHandler handler = fac.GetStaticFieldSetDelegate(field);
91
handler(new object[] { 5 });
92
Check.Assert(TestClass.StaticIntField == 5);
93
handler = fac.GetStaticFieldGetDelegate(field);
94
Check.Assert(((int)handler(null)) == 5);
95
}
96
97
public static void TestStaticProperty()
98
{
99
TestClass.StaticIntField = -1;
100
PropertyInfo property = typeof(TestClass).GetProperty("StaticIntProperty"); ;
101
StaticDynamicMethodProxyHandler handler = fac.GetStaticMethodDelegate(property.GetSetMethod());
102
handler(new object[] { 5 });
103
Check.Assert(TestClass.StaticIntProperty == 5);
104
handler = fac.GetStaticMethodDelegate(property.GetGetMethod());
105
Check.Assert(((int)handler(null)) == 5);
106
}
107
108
public static void TestNonStaticMethod()
109
{
110
TestClass obj = new TestClass();
111
112
DynamicMethodProxyHandler handler = fac.GetMethodDelegate(typeof(TestClass).GetMethod("NonStaticReturnVoidMethod"));
113
handler(obj, null);
114
115
object[] inputParams = new object[] { "str", 1, null, null };
116
handler = fac.GetMethodDelegate(typeof(TestClass).GetMethod("NonStaticReturnIntMethod"));
117
object ret = handler(obj, inputParams);
118
Check.Assert(((int)inputParams[2]) == 2);
119
Check.Assert(((string)inputParams[3]) == "outstr");
120
Check.Assert(((int)ret) == 3);
121
}
122
123
public static void TestNonStaticField()
124
{
125
TestClass obj = new TestClass();
126
obj.NonStaticIntField = -1;
127
128
FieldInfo field = typeof(TestClass).GetField("NonStaticIntField"); ;
129
DynamicMethodProxyHandler handler = fac.GetFieldSetDelegate(field);
130
handler(obj, new object[] { 5 });
131
Check.Assert(obj.NonStaticIntField == 5);
132
handler = fac.GetFieldGetDelegate(field);
133
Check.Assert(((int)handler(obj, null)) == 5);
134
}
135
136
public static void TestNonStaticProperty()
137
{
138
TestClass obj = new TestClass();
139
obj.NonStaticIntField = -1;
140
141
PropertyInfo property = typeof(TestClass).GetProperty("NonStaticIntProperty"); ;
142
DynamicMethodProxyHandler handler = fac.GetMethodDelegate(property.GetSetMethod());
143
handler(obj, new object[] { 5 });
144
Check.Assert(obj.NonStaticIntField == 5);
145
handler = fac.GetMethodDelegate(property.GetGetMethod());
146
Check.Assert(((int)handler(obj, null)) == 5);
147
}
148
}
149
}
namespace DynamicMethodFactoryUnitTest2
{3
public class TestClass4
{5
public static void StaticReturnVoidMethod()6
{7
}8

9
public static int StaticReturnIntMethod(string str, int i, ref int refInt, ref string refStr)10
{11
Check.Assert(str == "str");12
Check.Assert(i == 1);13
Check.Assert(refInt == 3);14
Check.Assert(refStr == "instr");15

16
int ret = i + refInt;17
refInt = i + 1;18
refStr = "ref" + str;19

20
Check.Assert(refInt == 2);21
Check.Assert(ret == 4);22
Check.Assert(refStr == "refstr");23

24
return ret;25
}26

27
public static int StaticIntField;28

29
public static int StaticIntProperty30
{31
get32
{33
return StaticIntField;34
}35
set36
{37
StaticIntField = value;38
}39
}40

41
public void NonStaticReturnVoidMethod()42
{43
}44

45
public int NonStaticReturnIntMethod(string str, int i, out int outInt, out string outStr)46
{47
outInt = i + 1;48
Check.Assert(outInt == 2);49
outStr = "out" + str;50
Check.Assert(outStr == "outstr");51
return i + 2;52
}53

54
public int NonStaticIntField;55

56
public int NonStaticIntProperty57
{58
get59
{60
return NonStaticIntField;61
}62
set63
{64
NonStaticIntField = value;65
}66
}67
}68

69
public class UnitTest70
{71
private static DynamicMethodFactory fac = new DynamicMethodFactory();72

73
public static void TestStaticMethod()74
{75
StaticDynamicMethodProxyHandler handler = fac.GetStaticMethodDelegate(typeof(TestClass).GetMethod("StaticReturnVoidMethod"));76
handler(null);77

78
object[] inputParams = new object[] { "str", 1, 3, "instr" };79
handler = fac.GetStaticMethodDelegate(typeof(TestClass).GetMethod("StaticReturnIntMethod"));80
object ret = handler(inputParams);81
Check.Assert(((int)inputParams[2]) == 2);82
Check.Assert(((string)inputParams[3]) == "refstr");83
Check.Assert(((int)ret) == 4);84
}85

86
public static void TestStaticField()87
{88
TestClass.StaticIntField = -1;89
FieldInfo field = typeof(TestClass).GetField("StaticIntField"); ;90
StaticDynamicMethodProxyHandler handler = fac.GetStaticFieldSetDelegate(field);91
handler(new object[] { 5 });92
Check.Assert(TestClass.StaticIntField == 5);93
handler = fac.GetStaticFieldGetDelegate(field);94
Check.Assert(((int)handler(null)) == 5);95
}96

97
public static void TestStaticProperty()98
{99
TestClass.StaticIntField = -1;100
PropertyInfo property = typeof(TestClass).GetProperty("StaticIntProperty"); ;101
StaticDynamicMethodProxyHandler handler = fac.GetStaticMethodDelegate(property.GetSetMethod());102
handler(new object[] { 5 });103
Check.Assert(TestClass.StaticIntProperty == 5);104
handler = fac.GetStaticMethodDelegate(property.GetGetMethod());105
Check.Assert(((int)handler(null)) == 5);106
}107

108
public static void TestNonStaticMethod()109
{110
TestClass obj = new TestClass();111

112
DynamicMethodProxyHandler handler = fac.GetMethodDelegate(typeof(TestClass).GetMethod("NonStaticReturnVoidMethod"));113
handler(obj, null);114

115
object[] inputParams = new object[] { "str", 1, null, null };116
handler = fac.GetMethodDelegate(typeof(TestClass).GetMethod("NonStaticReturnIntMethod"));117
object ret = handler(obj, inputParams);118
Check.Assert(((int)inputParams[2]) == 2);119
Check.Assert(((string)inputParams[3]) == "outstr");120
Check.Assert(((int)ret) == 3);121
}122

123
public static void TestNonStaticField()124
{125
TestClass obj = new TestClass();126
obj.NonStaticIntField = -1;127

128
FieldInfo field = typeof(TestClass).GetField("NonStaticIntField"); ;129
DynamicMethodProxyHandler handler = fac.GetFieldSetDelegate(field);130
handler(obj, new object[] { 5 });131
Check.Assert(obj.NonStaticIntField == 5);132
handler = fac.GetFieldGetDelegate(field);133
Check.Assert(((int)handler(obj, null)) == 5);134
}135

136
public static void TestNonStaticProperty()137
{138
TestClass obj = new TestClass();139
obj.NonStaticIntField = -1;140

141
PropertyInfo property = typeof(TestClass).GetProperty("NonStaticIntProperty"); ;142
DynamicMethodProxyHandler handler = fac.GetMethodDelegate(property.GetSetMethod());143
handler(obj, new object[] { 5 });144
Check.Assert(obj.NonStaticIntField == 5);145
handler = fac.GetMethodDelegate(property.GetGetMethod());146
Check.Assert(((int)handler(obj, null)) == 5);147
}148
}149
}DynamicMethodFactory类还可以用于以非泛型方法的调用语法调用泛型方法,在之前的一篇文章介绍过,这里就不重复了,感兴趣的朋友可以参见:改进的以非泛型方式调用泛型方法”之基于DynamicMethod的实现。
有任何问题欢迎回复讨论。
//The End


浙公网安备 33010602011771号