zoukankan      html  css  js  c++  java
  • 改进的“以非泛型方式调用泛型方法”之基于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();


    下载测试源代码

  • 相关阅读:
    常见的消息队列中间件介绍
    关系型数据库和非关系型数据库区别、oracle与mysql的区别
    SQL Server 和 Oracle 以及 MySQL 数据库
    Redis,Memcache,MongoDb的特点与区别
    详解布隆过滤器的原理,使用场景和注意事项
    Redis缓存穿透、缓存击穿以及缓存雪崩
    RPC、HTTP、RESTful
    集群,分布式,微服务概念和区别理解
    电脑双屏变单屏后,界面显示问题
    JDK 15已发布,你所要知道的都在这里!
  • 原文地址:https://www.cnblogs.com/teddyma/p/684306.html
Copyright © 2011-2022 走看看