改进的“以非泛型方式调用泛型方法”之基于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 }
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 }
测试代码如下(基于在双鱼座原文的代码格式):
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();
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();
下载测试源代码