改进的“以非泛型方式调用泛型方法”之基于DynamicMethod的实现
本文针对双鱼座同志的以非泛型方式调用泛型方法一文,提出一种更通用的以非泛型方式调用泛型方法的实现——基于DynamicMethod的实现。
基于DynamicMethod的实现的优点是,执行性能和双鱼座的文中实现的第5种方案——动态生成的非泛型接口包装相当(因为都是基于Emit的),但是,避免了原文实现中必须额外定义接口、Delegate的需要,从而,非常通用,应该是目前所能想到最佳实现。
首先,贴出原文中的测试数据相对于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
检查参数的有效性
139
140
构造用于缓存的key
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
创建所有方法的参数的本地变量
193
194
从paramObjs参数中解析所有参数值到本地变量中
213
214
执行目标方法
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
检查参数的有效性139

140
构造用于缓存的key152

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
创建所有方法的参数的本地变量193

194
从paramObjs参数中解析所有参数值到本地变量中213

214
执行目标方法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
}测试代码如下(基于在双鱼座原文的代码格式):
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();下载测试源代码


浙公网安备 33010602011771号