改进的“以非泛型方式调用泛型方法”之基于DynamicMethod的实现
【IT168 技术文档】本文针对双鱼座同志的以非泛型方式调用泛型方法一文,提出一种更通用的以非泛型方式调用泛型方法的实现——基于DynamicMethod的实现。
基于DynamicMethod的实现的优点是,执行性能和双鱼座的文中实现的第5种方案——动态生成的非泛型接口包装相当(因为都是基于Emit的),但是,避免了原文实现中必须额外定义接口、Delegate的需要,从而,非常通用,应该是目前所能想到非常好的实现。
首先,贴出原文中的测试数据相对于DynamicMethod实现的比较和缺点:
| 方案 | 耗时 | 比对 | 其他优点 |
| 直接调用 | 18 | 1 | 不通用 |
| 泛型委托包装 | 43 | 2.39 | 不通用 |
| 反射 | 16538 | 918.78 | 通用,不需额外定义 |
| 非泛型接口包装 | 60 | 3.33 | 通用,需要额外定义并实现 |
| 动态生成的非泛型接口包装 | 72 | 4 | 通用,需要额外定义 |
| DynamicMethod实现 | 72 | 4 | 通用,无需额外定义 |
实现代码如下:
1
public abstract class DynamicMethodHelper
2
{
3
//该类不能实例化,只能静态调用
4
private DynamicMethodHelper()
{}
5
6
//通用的可变参数动态方法委托
7
public delegate object DynamicMethodDelegate(params object[] paramObjs);
8
9
private static Dictionary<string, DynamicMethodDelegate> cache = new Dictionary<string, DynamicMethodDelegate>();
10
11
private static void LoadIndex(ILGenerator gen, int index)
12
{
13
switch (index)
14
{
15
case 0:
16
gen.Emit(OpCodes.Ldc_I4_0);
17
break;
18
case 1:
19
gen.Emit(OpCodes.Ldc_I4_1);
20
break;
21
case 2:
22
gen.Emit(OpCodes.Ldc_I4_2);
23
break;
24
case 3:
25
gen.Emit(OpCodes.Ldc_I4_3);
26
break;
27
case 4:
28
gen.Emit(OpCodes.Ldc_I4_4);
29
break;
30
case 5:
31
gen.Emit(OpCodes.Ldc_I4_5);
32
break;
33
case 6:
34
gen.Emit(OpCodes.Ldc_I4_6);
35
break;
36
case 7:
37
gen.Emit(OpCodes.Ldc_I4_7);
38
break;
39
case 8:
40
gen.Emit(OpCodes.Ldc_I4_8);
41
break;
42
default:
43
if (index < 128)
44
{
45
gen.Emit(OpCodes.Ldc_I4_S, index);
46
}
47
else
48
{
49
gen.Emit(OpCodes.Ldc_I4, index);
50
}
51
break;
52
}
53
}
54
55
private static void StoreLocal(ILGenerator gen, int index)
56
{
57
switch (index)
58
{
59
case 0:
60
gen.Emit(OpCodes.Stloc_0);
61
break;
62
case 1:
63
gen.Emit(OpCodes.Stloc_1);
64
break;
65
case 2:
66
gen.Emit(OpCodes.Stloc_2);
67
break;
68
case 3:
69
gen.Emit(OpCodes.Stloc_3);
70
break;
71
default:
72
if (index < 128)
73
{
74
gen.Emit(OpCodes.Stloc_S, index);
75
}
76
else
77
{
78
gen.Emit(OpCodes.Stloc, index);
79
}
80
break;
81
}
82
}
83
84
private static void LoadLocal(ILGenerator gen, int index)
85
{
86
switch (index)
87
{
88
case 0:
89
gen.Emit(OpCodes.Ldloc_0);
90
break;
91
case 1:
92
gen.Emit(OpCodes.Ldloc_1);
93
break;
94
case 2:
95
gen.Emit(OpCodes.Ldloc_2);
96
break;
97
case 3:
98
gen.Emit(OpCodes.Ldloc_3);
99
break;
100
default:
101
if (index < 128)
102
{
103
gen.Emit(OpCodes.Ldloc_S, index);
104
}
105
else
106
{
107
gen.Emit(OpCodes.Ldloc, index);
108
}
109
break;
110
}
111
}
112
113
public static DynamicMethodDelegate GetDynamicMethodDelegate(MethodInfo genericMethodInfo,
114
params Type[] genericParameterTypes)
115
{
116
检查参数的有效性#region 检查参数的有效性
117
118
if (genericMethodInfo == null)
119
{
120
throw new ArgumentNullException("需要被调用的方法的genericMethodInfo不能为空!");
121
}
122
123
if (genericParameterTypes != null)
124
{
125
if (genericParameterTypes.Length != genericMethodInfo.GetGenericArguments().Length)
126
{
127
throw new ArgumentException("genericMethodInfo和泛型参数类型的数量不一致!");
128
}
129
}
130
else
131
{
132
if (genericMethodInfo.GetGenericArguments().Length > 0)
133
{
134
throw new ArgumentException("没有为genericMethodInfo指定泛型参数类型!");
135
}
136
}
137
138
#endregion
139
140
构造用于缓存的key#region 构造用于缓存的key
141
142
string key = genericMethodInfo.DeclaringType.ToString() + "|" + genericMethodInfo.ToString();
143
if (genericParameterTypes != null)
144
{
145
for (int i = 0; i < genericParameterTypes.Length; ++i)
146
{
147
key += "|" + genericParameterTypes[i].ToString();
148
}
149
}
150
151
#endregion
152
153
DynamicMethodDelegate dmd;
154
155
lock (cache)
156
{
157
if (cache.ContainsKey(key))
158
{
159
dmd = cache[key];
160
}
161
else
162
{
163
//动态创建一个封装了泛型方法调用的非泛型方法,并返回绑定到他的DynamicMethodDelegate的实例
164
//返回的动态方法的实现在编译期就是以显式方法调用泛型方法的,因此,最大程度上避免了反射的性能损失
165
DynamicMethod dm = new DynamicMethod(Guid.NewGuid().ToString("N"),
166
typeof(object),
167
new Type[]
{ typeof(object[]) },
168
typeof(string).Module);
169
170
ILGenerator il = dm.GetILGenerator();
171
172
创建所有方法的参数的本地变量#region 创建所有方法的参数的本地变量
173
174
//首先获得目标方法的MethodInfo
175
MethodInfo makeGenericMethodInfo;
176
if (genericParameterTypes != null && genericParameterTypes.Length > 0)
177
{
178
makeGenericMethodInfo = genericMethodInfo.MakeGenericMethod(genericParameterTypes);
179
}
180
else
181
{
182
makeGenericMethodInfo = genericMethodInfo;
183
}
184
185
//声明本地变量
186
ParameterInfo[] pis = makeGenericMethodInfo.GetParameters();
187
for (int i = 0; i < pis.Length; ++i)
188
{
189
il.DeclareLocal(pis[i].ParameterType);
190
}
191
192
#endregion
193
194
从paramObjs参数中解析所有参数值到本地变量中#region 从paramObjs参数中解析所有参数值到本地变量中
195
196
for (int i = 0; i < pis.Length; ++i)
197
{
198
il.Emit(OpCodes.Ldarg_0);
199
LoadIndex(il, i);
200
il.Emit(OpCodes.Ldelem_Ref);
201
if (pis[i].ParameterType.IsValueType)
202
{
203
il.Emit(OpCodes.Unbox_Any, pis[i].ParameterType);
204
}
205
else if (pis[i].ParameterType != typeof(object))
206
{
207
il.Emit(OpCodes.Castclass, pis[i].ParameterType);
208
}
209
StoreLocal(il, i);
210
}
211
212
#endregion
213
214
执行目标方法#region 执行目标方法
215
216
for (int i = 0; i < pis.Length; ++i)
217
{
218
LoadLocal(il, i);
219
}
220
if (makeGenericMethodInfo.IsStatic)
221
{
222
il.Emit(OpCodes.Call, makeGenericMethodInfo);
223
}
224
else
225
{
226
throw new NotImplementedException("暂时还没有实现该功能!");
227
}
228
229
if (makeGenericMethodInfo.ReturnType == typeof(void))
230
{
231
il.Emit(OpCodes.Ldnull);
232
}
233
234
#endregion
235
236
il.Emit(OpCodes.Ret);
237
238
dmd = (DynamicMethodDelegate)dm.CreateDelegate(typeof(DynamicMethodDelegate));
239
cache.Add(key, dmd);
240
}
241
}
242
243
return dmd;
244
}
245
}
public abstract class DynamicMethodHelper2

{3
//该类不能实例化,只能静态调用4

private DynamicMethodHelper()
{}5

6
//通用的可变参数动态方法委托7
public delegate object DynamicMethodDelegate(params object[] paramObjs);8

9
private static Dictionary<string, DynamicMethodDelegate> cache = new Dictionary<string, DynamicMethodDelegate>();10

11
private static void LoadIndex(ILGenerator gen, int index)12

{13
switch (index)14

{15
case 0:16
gen.Emit(OpCodes.Ldc_I4_0);17
break;18
case 1:19
gen.Emit(OpCodes.Ldc_I4_1);20
break;21
case 2:22
gen.Emit(OpCodes.Ldc_I4_2);23
break;24
case 3:25
gen.Emit(OpCodes.Ldc_I4_3);26
break;27
case 4:28
gen.Emit(OpCodes.Ldc_I4_4);29
break;30
case 5:31
gen.Emit(OpCodes.Ldc_I4_5);32
break;33
case 6:34
gen.Emit(OpCodes.Ldc_I4_6);35
break;36
case 7:37
gen.Emit(OpCodes.Ldc_I4_7);38
break;39
case 8:40
gen.Emit(OpCodes.Ldc_I4_8);41
break;42
default:43
if (index < 128)44

{45
gen.Emit(OpCodes.Ldc_I4_S, index);46
}47
else48

{49
gen.Emit(OpCodes.Ldc_I4, index);50
}51
break;52
}53
}54

55
private static void StoreLocal(ILGenerator gen, int index)56

{57
switch (index)58

{59
case 0:60
gen.Emit(OpCodes.Stloc_0);61
break;62
case 1:63
gen.Emit(OpCodes.Stloc_1);64
break;65
case 2:66
gen.Emit(OpCodes.Stloc_2);67
break;68
case 3:69
gen.Emit(OpCodes.Stloc_3);70
break;71
default:72
if (index < 128)73

{74
gen.Emit(OpCodes.Stloc_S, index);75
}76
else77

{78
gen.Emit(OpCodes.Stloc, index);79
}80
break;81
}82
}83

84
private static void LoadLocal(ILGenerator gen, int index)85

{86
switch (index)87

{88
case 0:89
gen.Emit(OpCodes.Ldloc_0);90
break;91
case 1:92
gen.Emit(OpCodes.Ldloc_1);93
break;94
case 2:95
gen.Emit(OpCodes.Ldloc_2);96
break;97
case 3:98
gen.Emit(OpCodes.Ldloc_3);99
break;100
default:101
if (index < 128)102

{103
gen.Emit(OpCodes.Ldloc_S, index);104
}105
else106

{107
gen.Emit(OpCodes.Ldloc, index);108
}109
break;110
}111
}112

113
public static DynamicMethodDelegate GetDynamicMethodDelegate(MethodInfo genericMethodInfo, 114
params Type[] genericParameterTypes)115

{116

检查参数的有效性#region 检查参数的有效性117

118
if (genericMethodInfo == null)119

{120
throw new ArgumentNullException("需要被调用的方法的genericMethodInfo不能为空!");121
}122

123
if (genericParameterTypes != null)124

{125
if (genericParameterTypes.Length != genericMethodInfo.GetGenericArguments().Length)126

{127
throw new ArgumentException("genericMethodInfo和泛型参数类型的数量不一致!");128
}129
}130
else131

{132
if (genericMethodInfo.GetGenericArguments().Length > 0)133

{134
throw new ArgumentException("没有为genericMethodInfo指定泛型参数类型!");135
}136
}137

138
#endregion139

140

构造用于缓存的key#region 构造用于缓存的key141

142
string key = genericMethodInfo.DeclaringType.ToString() + "|" + genericMethodInfo.ToString();143
if (genericParameterTypes != null)144

{145
for (int i = 0; i < genericParameterTypes.Length; ++i)146

{147
key += "|" + genericParameterTypes[i].ToString();148
}149
}150

151
#endregion152

153
DynamicMethodDelegate dmd;154

155
lock (cache)156

{157
if (cache.ContainsKey(key))158

{159
dmd = cache[key];160
}161
else162

{163
//动态创建一个封装了泛型方法调用的非泛型方法,并返回绑定到他的DynamicMethodDelegate的实例164
//返回的动态方法的实现在编译期就是以显式方法调用泛型方法的,因此,最大程度上避免了反射的性能损失165
DynamicMethod dm = new DynamicMethod(Guid.NewGuid().ToString("N"), 166
typeof(object), 167

new Type[]
{ typeof(object[]) }, 168
typeof(string).Module);169

170
ILGenerator il = dm.GetILGenerator();171

172

创建所有方法的参数的本地变量#region 创建所有方法的参数的本地变量173

174
//首先获得目标方法的MethodInfo175
MethodInfo makeGenericMethodInfo;176
if (genericParameterTypes != null && genericParameterTypes.Length > 0)177

{178
makeGenericMethodInfo = genericMethodInfo.MakeGenericMethod(genericParameterTypes);179
}180
else181

{182
makeGenericMethodInfo = genericMethodInfo;183
}184

185
//声明本地变量186
ParameterInfo[] pis = makeGenericMethodInfo.GetParameters();187
for (int i = 0; i < pis.Length; ++i)188

{189
il.DeclareLocal(pis[i].ParameterType);190
}191

192
#endregion193

194

从paramObjs参数中解析所有参数值到本地变量中#region 从paramObjs参数中解析所有参数值到本地变量中195

196
for (int i = 0; i < pis.Length; ++i)197

{198
il.Emit(OpCodes.Ldarg_0);199
LoadIndex(il, i);200
il.Emit(OpCodes.Ldelem_Ref);201
if (pis[i].ParameterType.IsValueType)202

{203
il.Emit(OpCodes.Unbox_Any, pis[i].ParameterType);204
}205
else if (pis[i].ParameterType != typeof(object))206

{207
il.Emit(OpCodes.Castclass, pis[i].ParameterType);208
}209
StoreLocal(il, i);210
}211

212
#endregion213

214

执行目标方法#region 执行目标方法215

216
for (int i = 0; i < pis.Length; ++i)217

{218
LoadLocal(il, i);219
}220
if (makeGenericMethodInfo.IsStatic)221

{222
il.Emit(OpCodes.Call, makeGenericMethodInfo);223
}224
else225

{226
throw new NotImplementedException("暂时还没有实现该功能!");227
}228

229
if (makeGenericMethodInfo.ReturnType == typeof(void))230

{231
il.Emit(OpCodes.Ldnull);232
}233

234
#endregion235

236
il.Emit(OpCodes.Ret);237

238
dmd = (DynamicMethodDelegate)dm.CreateDelegate(typeof(DynamicMethodDelegate));239
cache.Add(key, dmd);240
}241
}242

243
return dmd;244
}245
}测试代码如下(基于在双鱼座原文的代码格式):
1
List<int> list = new List<int>();
2
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
3
watch.Reset();
4
watch.Start();
5
for (int i = 0; i < REPEAT_TIME; i++)
6
{
7
Program.Add<int>(i, list);
8
}
9
watch.Stop();
10
long l1 = watch.ElapsedMilliseconds;
11
watch.Reset();
12
MethodInfo mi = typeof(Program).GetMethod("Add");
13
DynamicMethodHelper.DynamicMethodDelegate dmd = DynamicMethodHelper.GetDynamicMethodDelegate(mi, typeof(int));
14
watch.Start();
15
for (int i = 0; i < REPEAT_TIME; i++)
16
{
17
dmd(i, list);
18
}
19
watch.Stop();
20
long l2 = watch.ElapsedMilliseconds;
21
Console.WriteLine("{0}\n{1} vs {2}", list.Count, l1, l2);
22
Console.ReadLine();
List<int> list = new List<int>();2
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();3
watch.Reset();4
watch.Start();5
for (int i = 0; i < REPEAT_TIME; i++)6

{7
Program.Add<int>(i, list);8
}9
watch.Stop();10
long l1 = watch.ElapsedMilliseconds;11
watch.Reset();12
MethodInfo mi = typeof(Program).GetMethod("Add");13
DynamicMethodHelper.DynamicMethodDelegate dmd = DynamicMethodHelper.GetDynamicMethodDelegate(mi, typeof(int));14
watch.Start();15
for (int i = 0; i < REPEAT_TIME; i++)16

{17
dmd(i, list);18
}19
watch.Stop();20
long l2 = watch.ElapsedMilliseconds;21
Console.WriteLine("{0}\n{1} vs {2}", list.Count, l1, l2);22
Console.ReadLine();0
相关文章