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
记录个人一直以来对枚举定义和使用的两个误解