本文介绍一组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的代码
1
private
static
void
LoadParameters(CodeGenerator gen, ParameterInfo[] pis,
bool
isMethodStatic)
2
{
3
Check.Require(gen,
"
gen
"
);
4
5
if
(pis
!=
null
)
6
{
7
for
(
int
i
=
0
; i
<
pis.Length;
++
i)
8
{
9
if
(isMethodStatic)
10
{
11
gen.Ldarg(
0
);
12
}
13
else
14
{
15
gen.Ldarg(
1
);
16
}
17
gen.Ldc(i);
18
19
Type srcType
=
pis[i].ParameterType;
20
string
str
=
srcType.ToString();
21
if
(str.EndsWith(
"
&
"
))
22
{
23
srcType
=
CommonUtils.GetType(str.Substring(
0
, str.Length
-
1
));
24
}
25
26
if
(str.EndsWith(
"
&
"
))
//
ref or out param
27
{
28
if
(srcType.IsValueType
&&
(pis[i].Attributes
&
ParameterAttributes.Out)
!=
ParameterAttributes.Out)
//
ref value param
29
{
30
gen.Ldelem(
typeof
(
object
));
31
gen.Unbox(srcType);
32
}
33
else
34
{
35
if
(srcType.IsValueType
&&
srcType
!=
typeof
(
object
))
//
out value param
36
{
37
gen.LoadDefaultValue(srcType);
38
gen.Box(srcType);
39
gen.Stelem(
typeof
(
object
));
40
41
if
(isMethodStatic)
42
{
43
gen.Ldarg(
0
);
44
}
45
else
46
{
47
gen.Ldarg(
1
);
48
}
49
gen.Ldc(i);
50
gen.Ldelem(
typeof
(
object
));
51
gen.Unbox(srcType);
52
}
53
else
//
ref or out class param
54
{
55
gen.Ldelema(
typeof
(
object
));
56
}
57
}
58
}
59
else
60
{
61
gen.Ldelem(
typeof
(
object
));
62
63
if
(srcType.IsValueType)
64
{
65
gen.UnboxAny(srcType);
66
}
67
else
if
(srcType
!=
typeof
(
object
))
68
{
69
gen.Castclass(srcType);
70
}
71
}
72
}
73
}
74
}
75
76
private
static
void
CastValueToObject(CodeGenerator gen, Type valueType)
77
{
78
if
(valueType
==
typeof
(
void
))
79
{
80
gen.Load(
null
);
81
}
82
else
if
(valueType.IsValueType)
83
{
84
gen.Box(valueType);
85
}
86
else
if
(valueType
!=
typeof
(
object
))
87
{
88
gen.Castclass(
typeof
(
object
));
89
}
90
}
代码是不是相对比较简单呢(当然是相对于自己写所有的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
#region
Check preconditions
7
8
Check.Require(targetModule,
"
targetModule
"
);
9
Check.Require(genericMethodInfo,
"
genericMethodInfo
"
);
10
Check.Require((genericParameterTypes
==
null
&&
genericMethodInfo.GetGenericArguments().Length
==
0
)
||
11
genericParameterTypes.Length
==
genericMethodInfo.GetGenericArguments().Length,
12
"
The number of generic type parameter of genericMethodInfo and the input types must equal!
"
);
13
Check.Require(
!
genericMethodInfo.IsStatic,
"
genericMethodInfo must not be static here!
"
);
14
15
#endregion
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

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27


1

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

代码是不是相对比较简单呢(当然是相对于自己写所有的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