记录个人一直以来对枚举定义和使用的两个误解

1、枚举定义声明基础类型的限制

想要定义一个表示数据库主键编号范围的枚举:

    /// <summary>
    /// 编号范围枚举
    /// </summary>
    public enum IDRangeType : Int64
    {
        /// <summary>
        /// 1到2的32次方
        /// </summary>
        [Description("1到2的32次方")]
        Between1ToPowerOf32 = 2147483748,

        /// <summary>
        /// 2的32次方到2的40次方
        /// </summary>
        [Description("2的32次方到2的40次方")]
        BetweenPowerOf32ToPowerOf40 = 2199023255552,

        /// <summary>
        /// 2的40次方以上
        /// </summary>
        [Description("2的40次方以上")]
        Bigger = Int64.MaxValue,

    }

但是上面这种声明直接导致编译错误:应输入类型 byte、sbyte、short、ushort、int、uint、long 或 ulong 

也就是说,枚举的基础类型只能为8种数字类型: byte、sbyte、short、ushort、int、uint、long 或 ulong。声明为其他类型如Int16、Int32、Int64等都不行。

按照VS编译提示,将Int64改为long,果然通过,真是奇哉怪也。

我们平时理解的Int64和long其实在MS .Net Framework中是没有区别的,long只是Int64的一个别名而已(而Java的基元值类型的包装类都是引用类型),而且Framework编程规范里还明确说推荐使用Int64,这样可以保证跨语言或者跨平台代码移植方便。但是在枚举声明这里,只能使用别名。

顺带再提一下.NET Framework中非常特殊的一个类型System.Enum,它是个引用类型,Framework中将System.Enum定义为一个抽象类,但是它又继承自System.ValueType。

通过类型判断,却又发现它不是ValueType,而且也不是枚举:

            var num = new Int32();
            Console.WriteLine(num is ValueType); //True
            Console.WriteLine(num.GetType().IsValueType); //True

            var type = typeof(System.Enum);
            Console.WriteLine(type.IsValueType); //False ???
            Console.WriteLine(type.IsEnum); //False ???

C#语言特性中有很多特例存在,System.Enum即为一例。

 

2、web服务的客户端代理和服务端的枚举数值定义不一致

还以上面的枚举作为示例,我们要在一个标识为WebMethod的web服务方法中使用这个枚举,新建一个web服务并部署好以后供客户端调用。

通过WSDL工具,直接将这个web服务生成保存为本地代理类,然后查看代理类源代码,客户端代理类生成的枚举IDRangeType竟然变成:

    public enum IDRangeType : long
    {

        Between1ToPowerOf32,


        BetweenPowerOf32ToPowerOf40,


        Bigger,

    }

客户端生成的枚举,没有把服务端枚举定义中显式定义的数值带过来。对于IDRangeType这种定义枚举就是要使用枚举的数值而言,简直太出乎人的意料之外。

然后想到可能是序列化和反序列化的问题,尝试着给枚举属性分别加上特性DataMember和EnumMember,问题依旧。但在WCF试验中发现一切正常,打开WCF生成的客户端代理类,枚举数值的定义和服务端没什么变化。

后来想想又搞不明白,枚举既然是继承自基元值类型,那么值类型怎么序列化,枚举也应该像基元值类型一样序列化才对,而且一直说服务分享 Schema(for structures) 和 Contract(for behaviors), 而不是 Class,难道枚举不是Schema和Contract的一部分,或者是SOAP的.NET实现不支持枚举?

试验多次久久不能解决问题,最后搜索一下.net web服务和枚举这两个关键字,发现果然很早就有一篇流传甚广的Web Services and C# Enums文章讲到“Numeric Values Are Not Preserved”这个事情。文章还有提到,在web服务中,Flag标记下的枚举在客户端生成的时候数值改变,很容易导致灾难后果(This can lead to disastrous consequences)。

通过这个问题,让我深深意识到服务端和客户端生成代码的差异,不同环境不同应用场景下,有些特殊情况很容易偏离习惯认知和主观判断,必须多尝试实践才能出真知。

 

参考:

http://ikriv.com/dev/dotnet/webservices_and_enums.html

http://msdn.microsoft.com/zh-cn/library/System.Web.Services(v=vs.110).aspx

http://msdn.microsoft.com/zh-cn/library/sbbt4032.aspx

http://msdn.microsoft.com/zh-cn/library/aa347875(v=vs.110).aspx

posted on 2014-06-25 20:39  JeffWong  阅读(2591)  评论(7编辑  收藏  举报