改进的“以非泛型方式调用泛型方法”之基于DynamicMethod的实现

本文针对双鱼座同志的以非泛型方式调用泛型方法一文,提出一种更通用的以非泛型方式调用泛型方法的实现——基于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    }

测试代码如下(基于在双鱼座原文的代码格式):

 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();


下载测试源代码

posted @ 2007-03-22 17:06  Teddy's Knowledge Base  Views(10295)  Comments(8Edit  收藏  举报