zoukankan      html  css  js  c++  java
  • 快速类型判定

        最近在写一段代码的时候,为了兼容各种未知的类型,以及完成一个根据类型自动分派的任务到对应的处理器时,使用了这样的一个契约:

        用一个object数组来转递值,每个处理器则声明一个可以处理的类型组合。

        然后在写如何分派时,发生了一段小插曲。

    分派原则

        首先,明确一下分派的原则,如果处理器声明能处理的类型是:

    string,int

        那么只有当对象数组的长度为2,并且类型分别为string和int时,才会分派到这个处理器上去执行,不过,要注意的一点是,string是引用类型,因此值可以为null,最终,声明为string,int的处理器可以接受下列参数:

    "aaa",123

    null,123

        其他形式的参数,不应该分派给该处理器。

        然后,再说说更复杂的情况,如果处理器声明能处理的类型是:

    IClonable,int?

        那么,问题要稍微复杂一点,对于第一个参数而言,要求是任意实现了IClonable接口的对象(例如任何string,数组),当然null也是允许的。对于第二个参数而言,int?可以接受任意int或者null。

        综合起来,也就是下列值是允许的:

    "aaa”, 123

    new int[0], null

    null, null

        最后,给两个特殊的契约,如果可以传递任意参数,那么就指定object,如果必须某一项为null,则类型指定null。

    基础实现

        统一好上面的契约,开始实现时,发现事情没有想象中这么简单,对于值为null的情况,需要判断类型是否是引用类型(!Type.IsValueType),如果是值类型,还要额外允许可空类型(Nullable.GetUnderlyingType)。

        对于非可空的情况,则判断继承树(Type.IsAssignableFrom),在加上其他的逻辑,代码也不短,更要命的是其中用到了N多的反射,而且每次都要反射一把,性能绝对是个问题。

        (如果各位有兴趣,可以自己实现一下,这里就不写了)

    思路

        解决这些问题之前,先想一下,如果这些类型在编译时已知,那么代码会怎么写哪?

        假设需要判定对象obj中间的类型是不是string,那么会写下面的代码:

    obj is string

        是不是IClonable,那么会写下面的代码:

    obj is IClonable

        是不是int,那么会写下面的代码:

    obj is int

        是不是int?,那么会写下面的代码:

    obj is int?

        看到这里发现什么了?在c#中这些判定惊人的一致,唯一需要特殊处理的是null。所以想象一下,如果能把动态的类型实例,转换成静态的类型信息,对object数组的类型判定将变得非常简单。

    进阶实现

        那么运用什么手段可以将动态类型实例变成静态的类型信息哪?

        答案有很多,当然都围绕一个主题——动态代码生成

        这里用最轻量级的动态代码生成方式Emit实现的,具体过程就略过了,最终的核心代码部分如下:

            public static Func<object[], bool> CreateFastTypeChecker(this Type[] expectedTypes)
    {
    // check parameters ...
    DynamicMethod dm = new DynamicMethod("Zhenway's Fast Type Checker", typeof(bool), new Type[] { typeof(object[]) });
    var il = dm.GetILGenerator();
    Label retFalse = il.DefineLabel();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldlen);
    il.Emit(OpCodes.Ldc_I4, expectedTypes.Length);
    il.Emit(OpCodes.Bne_Un, RetFalse);
    for (int index = 0; index < expectedTypes.Length; index++)
    {
    bool mustBeNull = expectedTypes[index] == null;
    bool mustNotBeNull = !mustBeNull && expectedTypes[index].IsValueType &&
    Nullable.GetUnderlyingType(expectedTypes[index]) == null;
    if (expectedTypes[index] == typeof(object))
    continue;
    Label Next = il.DefineLabel();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldc_I4, index);
    il.Emit(OpCodes.Ldelem, typeof(object));
    il.Emit(OpCodes.Ldnull);
    il.Emit(OpCodes.Cgt_Un);
    if (mustNotBeNull)
    il.Emit(OpCodes.Brfalse, retFalse);
    else
    il.Emit(OpCodes.Brfalse_S, Next);
    if (mustBeNull)
    {
    il.Emit(OpCodes.Br, RetFalse);
    }
    else
    {
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldc_I4, index);
    il.Emit(OpCodes.Ldelem, typeof(object));
    il.Emit(OpCodes.Isinst, expectedTypes[index]);
    il.Emit(OpCodes.Ldnull);
    il.Emit(OpCodes.Cgt_Un);
    il.Emit(OpCodes.Brfalse, RetFalse);
    }
    il.MarkLabel(Next);
    }
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Ret);
    il.MarkLabel(retFalse);
    il.Emit(OpCodes.Ldc_I4_0);
    il.Emit(OpCodes.Ret);
    return (Func<object[], bool>)dm.CreateDelegate(typeof(Func<object[], bool>));
    }
    整个过程只需要花50行左右的代码,但是却可以带来非常高的执行效率(当然,创建成本比较高,如果,只跑一两次,那就亏了)。
  • 相关阅读:
    1202诗人基本介绍&诗人画像
    1205人物关系优化&诗人轨迹
    把SQL Server 2000 表中的自动编号Id重新开始排列
    一个一直都不明白的东西今天知道了。关于sqlserver2000自动执行。
    服装打版界的扛把子ET自定义操作
    手把手教你搭建集中式版本控制系统SVN服务器
    分享一次实用的爬虫经验
    盘点CSV文件在Excel中打开后乱码问题的两种处理方法
    盘点服装设计所经常性使用的软件ET(下篇)
    sql 每个企业选择一条产品
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/1909388.html
Copyright © 2011-2022 走看看