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

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

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

时间: 2024-10-25 11:16:17

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

.NET MVC4 实训记录之三(EntityFramework 与枚举)

EntityFramework对枚举的引入是从版本5开始的(如果没有记错的话).枚举可以很大程度上提高对程序的可读性.那么在EntityFramework的CodeFirst模式下,如何使用枚举呢?答案很简单:还是那么用! 看似废话,其实不然,看下面(修改上一篇中用户信息定义): /// <summary> /// 性别枚举 /// </summary> public enum Gender { Male, Female } public class UserProfile { [

UIView的几个枚举定义

UIView是iOS开发最主要的视图,非常多控件都是继承它,掌握当中的几个基本枚举定义,有利益理解视图的载入和參数差别. 一.UIViewAnimationCurve UIView的基本动画变化规律 typedef NS_ENUM(NSInteger, UIViewAnimationCurve) { UIViewAnimationCurveEaseInOut, // 淡入淡出 UIViewAnimationCurveEaseIn, // 開始时慢 UIViewAnimationCurveEase

Codeforces Round #283 (Div. 2) A. Minimum Difficulty【一个数组定义困难值是两个相邻元素之间差的最大值。 给一个数组,可以去掉任意一个元素,问剩余数列的困难值的最小值是多少】

A. Minimum Difficulty time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Mike is trying rock climbing but he is awful at it. There are n holds on the wall, i-th hold is at height ai off the g

C++ 枚举定义

我们在平常的编程中,时常需要为一些属性定义一组可以选择的值,比如文件打开的状态可能会有三种:输入 输出和追加 我们一般情况下记录这些状态是让每一个状态和一个常数相对应   比如 1 const int input=0; 2 const int output=1; 3 const int append=2; 这个方法虽然也是可以得,不过它有一个明显的缺点就是    没有指出这些值是相关联的 而C++中的  枚举  提供了一种替代的方法   不但可以定义常数集   还可以将其聚集成组    如下:

C# 给枚举定义DescriptionAttribute,把枚举转换为键值对

在C#中,枚举用来定状态值很方便,例如我定义一个叫做Season的枚举 public enum Season { Spring = 1, Summer = 2, Autumn = 3, Winter = 4 } 枚举名是不能出现空格,()-/等字符 我们想把Spring显示为春天,我们要自己定义说明信息,我们可以使用DescriptionAttribute,如下 public enum Season { [Description("春 天")] Spring = 1, [Descrip

枚举 定义其他结构

枚举是多例设计 要求构造方法私有化 枚举中定义的构造方法不能用public 枚举对象必须放在首行,随后才可以定义属性,构造方法,普通方法 package cn; enum Color{ //定义好了枚举类 RED("红色") ,GREEN ("绿色"),BLUE("蓝色") ; //枚举对象dingyi8在最上面 private String title ; //属性 private Color(String title){ // 私有 this

oc中的枚举定义

typedef NS_ENUM(类型,枚举名){        枚举名+值名,       枚举名+值名,}; 该方法定义的枚举,OC会自动把其转换成合适当前版本的枚举.如果枚举值可合并的话 NS_ENUM 要改成NS_OPTIONS,枚举值默认为0-n也可通过在值名后加入=number方式自定义.

C++中枚举定义运算符

由于枚举也是用户定义类型,所以是可以定义运算符, 如: 1 enum Day {sun, mon, tue, wen, thu, fri, sat}; 2 3 Day& operator++(Day& d) 4 { 5 return d = (sat == d) ? sun : Day(sta + 1); 6 } 可以这样来使用: Day today = sun; ++today; // mon

枚举定义,获取枚举的描述

定义枚举熟悉,描述 /// <summary> /// 枚举注释的自定义属性类 /// </summary> public class EnumDescriptionAttribute : Attribute { private string m_strDescription; public EnumDescriptionAttribute(string strPrinterName) { m_strDescription = strPrinterName; } public st