想做一个通用的byte转换enum转换算法,检查enum是否合法 ,结果发现效率很低,只能这样写:
       private T GetEnumFromByte<T>(byte b)  {
            if (Enum.IsDefined(typeof(T), b)) {
                return (T)Enum.Parse(typeof(T), b.ToString());
            }
            else {
                throw SgipException.CreateByErrorCode(5);
            }
        }
以下写法不能编译:
             private T GetEnumFromByte<T>(byte b) where T:struct {
            if (Enum.IsDefined(typeof(T), b)) {
                return (T)b;
            }
            else {
                throw SgipException.CreateByErrorCode(5);
            }
        }
不知道有没有更好的算法,因为这个算法太慢了。要把byte转成string,然后再用enum的parse方法转回来。我希望能直接转换,这样速度比较快。

解决方案 »

  1.   

    struct 是值类型,不能用于约束。
      

  2.   

    老兄,你动手试试看,不能编译的部分是byte不能转换为T,所以只好采用速度慢的办法。
    where 后面可以用class 和struct,但是不能用Enum和ValueType,int等类型
      

  3.   

    不是的,这个用于网络程序,每个包过来都要用到的。
    性能倒是其次,主要向探索一下值类型如何运用范型约束。而且所有的enum类型转换都有这个问题的。比如我可以把一个非法的值转换成一个enum,而系统运行时也不会报错,但是逻辑就不对了。
      

  4.   

    private T GetEnumFromByte<T>(byte b)
    {
        T value = (T)Convert.ChangeType(b, Enum.GetUnderlyingType(typeof(T)));
        if (Enum.IsDefined(typeof(T), value))
        {
            return value;
        }
        else
        {
            throw SgipException.CreateByErrorCode(5);
        }
    }别看用到了ChangeType,其实这里速度满快的,比Parse快
    但是你要保证T是一个Enum,若用C++/CLI可以让编译器帮你检查。
      

  5.   

    正解!这样可以通过编译了。我把修改后的通用的Enum合法性检查程序贴出来:
            private EnumTypeName GetEnumFromByte<EnumTypeName, IntTypeName>(IntTypeName b)
                where EnumTypeName : struct//enum一定要是一个Enum,此代码中不做检查
                where IntTypeName : struct {//intType可以是各种int类型,要与EnumType的基础类型一致,此处不做检查
                EnumTypeName value = (EnumTypeName)Convert.ChangeType(b, Enum.GetUnderlyingType(typeof(EnumTypeName)));
                if (Enum.IsDefined(typeof(EnumTypeName), value)) {
                    return value;
                }
                else {
                    throw new InvalidCastException();
                }
            }
    美中不足的是,Enum.IsDefined(typeof(EnumTypeName), value)这个地方还是有装箱操作,不如依次判断来得快。
    因为c#语法的限制,约束时只能限制enum为值类型,不能限制为enum类型。为了提高效率,没有在代码中检查。用的时候按照说明就可以了。
    比如 
    if(value!=(byte)Color.Red ||value!=(byte)Color.Blue ||value!=(byte)Color.Green){
    ...
    }
    不过,如果每个Enum都这样写的话,就难以写成通用的算法了。不知道还有没有更好的算法,能够避免装箱操作。
    暂时不结贴。要是谁给出更好的算法,赠送500分。
      

  6.   

    你不要把装箱看得太重,其实装箱在很大的Enum上作IsDefine已经很小了。
    Enum.EsDefined是用二分法查找的,性能是O(Enum元素个数),如果元素很多,那么这个方法比你逐个比较更有效!完全可以抵消掉装箱损失。
    况且你用于网络应用,我认为每个包两次的装箱/拆箱不算什么,不影响到你的性能。
      

  7.   

    Sorry,说错了,是O(Log(Enum元素个数))
      

  8.   

    http://www.microsoft.com/china/msdn/library/langtool/vcsharp/csharpgenerics.mspx?mfr=true
      

  9.   

    @zhaoliang_chen(龙行天下) 这篇文章翻译得一塌糊涂啊。。
      

  10.   

    这回没装箱了,但是速度未必比有装箱的快很多就是了private TEnum GetEnumFromByte<TEnum>(byte b)
    {
        Type enumType = typeof(TEnum);
        TEnum value = (TEnum)Convert.ChangeType(b, Enum.GetUnderlyingType(enumType));
        TEnum[] enumValues = (TEnum[])Enum.GetValues(enumType);    if (Array.BinarySearch(enumValues, value) > 0)
        {
            return value;
        }
        else
        {
            throw SgipException.CreateByErrorCode(5);
        }
    }PS.不用约束,因为即使约束,也不可能保证TEnum一定是枚举,而且性能也没有任何不同。
      

  11.   

    public class EnumConverter<Enumtype> {
            static Enumtype[] values;
            static Type enumType = typeof(Enumtype);
            static EnumConverter() {
                values = (Enumtype[]) Enum.GetValues(typeof(Enumtype));
            }
            public static Enumtype GetFromByte(byte value) {
                for (int i = 0; i < values.Length; i++) {
                    byte enumValue = (byte)((IConvertible)values[i]); ;
                    if (enumValue ==value) {
                        return values[i];
                    }
                }
                throw new InvalidCastException(string.Format("Can't convert from System.Byte to {0}", enumType.Name));
            }
            public static Enumtype GetFromInt(int value) {
                for (int i = 0; i < values.Length; i++) {
                    int enumValue = (int)((IConvertible)values[i]);
                    if (enumValue == value) {
                        return values[i];
                    }
                }
                throw new InvalidCastException(string.Format("Can't convert from System.Int32 to {0}", enumType.Name));
            }
    对于Enum,一般都只有十来个。对于这种情况,逐个比较应该比创建hashTable再查找来的快。
    此外,在静态构造方法中取出了Enum的值列表,避免了每次查找。这个算法比较满意了。
    不过,可能还是有装箱。只不过藏在系统的方法里面。遗憾的地方是不能使用指针。否则还可以再挖出一点效率。
      

  12.   

    public class EnumConverter<Enumtype,EnumBaseType> {
            static Enumtype[] values;
            static EnumBaseType[] intValues;
            static string enumTypeName = typeof(Enumtype).FullName;
            static string enumBaseTypeName = typeof(EnumBaseType).FullName;
            static EnumConverter() {
                values = (Enumtype[]) Enum.GetValues(typeof(Enumtype));
                intValues = new EnumBaseType[values.Length];
                for (int i = 0; i < values.Length;i++ ) {
                    EnumBaseType intValue = (EnumBaseType)(IConvertible)values[i];
                    intValues[i] = intValue;
                }
            }
     
            public static Enumtype GetEnum(EnumBaseType value) {
                for (int i = 0; i < intValues.Length; i++) {
                    if (((IComparable<EnumBaseType>)intValues[i]).CompareTo(value)==0) {
                        return values[i];
                    }
                }
                throw new InvalidCastException(string.Format("Can't convert from {0} to {1}", enumBaseTypeName,enumTypeName));
            }    }
    不知道这样在转换的方法中会不会装箱。得看看il。
      

  13.   

    我的方法里最慢的步骤恐怕就是
    TEnum[] enumValues = (TEnum[])Enum.GetValues(enumType);如果按你写的那样,把这个缓存进static域里,就非常完美了。但是我觉得没必要了,快不了多少。PS.要积极推行大写字母T开头定义类型参数的命名规则
      

  14.   

    楼主想要彻彻底底没有动态成分的做法也不是没有,Emit出来给你就是。
    但是觉得你追求速度也用不到这样吧。。
      

  15.   

    呵呵,我想研究一下。此外,我想看看能不能有办法在这种数字的运算中避免创建任何实例。
    实际上,我看了il,我的后一种方法,可能比前一种方法更慢。转型成IConvertible的时候,创建了一个instance. 而且代码变长了。而前一个方法只不过有一个box.
    确实,这种优化没有必要。因为在网关上最慢的操作是写日志。我也不提倡做这样的优化。
    不过,这个问题研究一下还是有必要的。因为这种int转换成Enum的操作很普遍。一次写好定型了,以后都会用得着。
    这个代码还是可以继续优化的。比如在枚举数量较多的情况下采用二分法查找,较少的时候逐个比较。还有完全避免创建任何instance。
    你的算法在枚举数量较多的情况下,肯定会比较快。但是一般情况下,枚举都不超过十个值。这时候逐个比较反而会快一些。
    Convert.ChangeType里面调用的代码有几次创建实例的操作。不会很快的。我也可以用二分法查找的,但是我担心BinarySearch会在执行过程中创建实例,这样就会抵消算法的优势。
    isDefine里面还不是二分法查找,而是hashtable,在数量较多的情况下,比二分法更快。但是创建实例的操作比较多。这个就抵消了算法上的优势。因为创建实例的速度要比数值计算慢一个数量级。(mSDN里面说慢20倍)。因为前者要执行一系列与分配内存有关的操作,尽管在il中只有以行代码,但是.net framework还要执行一系列分配内存的操作。而后者仅仅是几条机器指令。
    确实,我想把算法尽量优化到最大程度。因为我想证明c#不比c++慢。但是,要做到这一点,就得在保持代码外部接口不受影响的情况下,在内部尽量避免创建实例。算法一般是差不多的。
    c#的优势在于开发速度快,有精力在算法上做更好的优化。
    但是现在有个瓶颈,就是数据库insert执行的速度太慢。我做了测试,sqlserver在默认配置下,创建一个表,有十个nvarchar字段,递增主键。然后向表中连续插入10000条每个字段100个英文字母的字符串,这样每条记录实际长度是2004字节。c#每秒只能插入1300-1500条左右的数据。用vc+Ado也是一样,甚至还要更慢一些。用查询分析器执行的结果也差不多。
    把每个字段的长度减少到一个字节,插入速度并没有明显的提高。仅仅上升到2000条每秒左右。
    用批更新是行不通的,因为每个包都需要记日志。我Google了一下,发现大家差不多都是这个数字。
    这个问题,恐怕难以解决,不知道老兄有没有什么建议?此外,不知道tpcc上每分钟300万个交易的纪录(非集群),用的是什么技术?为什么相差这么大?