基于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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

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
}

2

3

4

5

6

16

17

18

19

20

21

22

23

24

25

26

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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

DynamicMethodFactory类还可以用于以非泛型方法的调用语法调用泛型方法,在之前的一篇文章介绍过,这里就不重复了,感兴趣的朋友可以参见:改进的以非泛型方式调用泛型方法”之基于DynamicMethod的实现。
有任何问题欢迎回复讨论。
//The End
分类:
NBear
, Tech. Thinking
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2005-10-10 AOP Practice with AspectWeaver0.6 - DebugPropertyValueModifying