改进的“以非泛型方式调用泛型方法”之基于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

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

139

140

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

193

194

213

214

235

236

237

238

239

240

241

242

243

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

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

下载测试源代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构