本文介紹一組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
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
