代码改变世界

C# Enum (枚举)

2009-12-01 09:47  Mr.Longer  阅读(6062)  评论(3编辑  收藏  举报

 enum 关键字用于声明枚举,即一种由一组称为枚举数列表的命名常数组成的独特类型。每种枚举类型都有基础类型,该类型可以是除 char 以外的任何整型。枚举元素的默认基础类型为 int。默认情况下,第一个枚举数的值为 0,后面每个枚举数的值依次递增 1。

Code:

enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

这样默认的值就是为 enum Days{0,1,2,3,4,5},假如您想指定默认的值为1开始,就必须 enum Days{Stat=1,Sun=2, .....}

注意: 枚举数的名称中不能包含空白。

1. 创建枚举时,应选择最合理的默认值并赋给它一个零值。这便使得只要在创建枚举时未为其显式赋值,则所创建的全部枚举都将具有该默认值。枚举中大小写敏感,但是建议不要这样。

2. 枚举是一种值类型 ,System.Enum 类型是所有枚举类型的抽象基类(它是一种与枚举类型的基础类型不同的独特类型),并且从 System.Enum 继承的成员在任何枚举类型中都可用。

存在从任何枚举类型到System.Enum 的装箱转换,并且存在从 System.Enum 到任何枚举类型的取消装箱转换。

System.Enum 本身不是枚举类型。相反,它是一个类类型,所有枚举类型都是从它派生的。

类型 System.Enum 从类型 System.ValueType派生,而后者又从类型 object 派生。在运行时,类型 System.Enum 的值可以是 null 或是对任何枚举类型的装了箱的值的引用。

基础类型指定为每个枚举数分配的存储大小。但是,从 enum 类型到整型的转换需要用显式类型转换来完成。例如,下面的语句通过使用强制转换从 enum 转换为 int,将枚举数 Sun 赋给 int 类型的变量:int x = (int)Days.Sun;

 一、enum的普通用法

1.平常的用法

 

代码

 

在此例中,使用了基类选项来声明成员类型是 long 的 enum。注意,即使枚举的基础类型是 long,枚举成员仍然必须使用强制转换显式转换为 long 类型。

2. 特殊的用法

Enum.Parse()方法。这个方法带3个参数,第一个参数是要使用的枚举类型。其语法是关键字typeof后跟放在括号中的枚举类名。第二个参数是要转换的字符串,第三个参数是一个bool,指定在进行转换时是否忽略大小写。最后,注意Enum.Parse()方法实际上返回一个对象引用—— 我们需要把这个字符串显式转换为需要的枚举类型(这是一个取消装箱操作的例子)。对于上面的代码,将返回1,作为一个对象,对应于TimeOfDay.Afternoon的枚举值。在显式转换为int时,会再次生成1。

 

代码

 

3.中文描述的用法

如果想要Enumeration返回一点有意义的string,从而用户能知道分别代表什么, 则按如下定义:

代码

或者可以自己定义Discription Attributes:(来自:James Geurts' Blog)

代码

 

 二、enum的高级用法

Enum的运算通常涉及到位运算(AND、OR、XOR),按位运算, 常使用FlagsAttribute 自定义属性定义。FlagsAttribute 和 Enum 的准则:

只有要对数值执行按位运算(AND、OR、XOR)时才对枚举使用 FlagsAttribute 自定义属性。
用 2 的幂(即 1、2、4、8 等)定义枚举常量。这意味着组合的枚举常量中的各个标志都不重叠。
请考虑为常用标志组合创建一个枚举常量。例如,如果用于文件 I/O 操作的枚举包含枚举常量 Read = 1 和 Write = 2,请考虑创建枚举常量 ReadWrite = Read OR Write,该常量组合了 Read 和 Write 标志。此外,在某些情况下,可能会将用于组合标志的按位 OR 运算视为一种高级概念,在简单任务中不需要执行此操作。
将负数定义为标志枚举常量时应谨慎,因为很多标志位置都可能设置为 1,这可能使您的代码产生混淆并易于发生代码错误。
测试数值中是否已设置标志的一种简便方法为:在数值和标志枚举常量之间执行按位“与”操作,这种方法会将数值中与标志不对应的所有位都设置为零,然后测试该操作的结果是否等于该标志枚举常量。
将 None 用作值为零的标志枚举常量的名称。在按位 AND 运算中,不能使用 None 枚举常量测试标志,因为所得的结果始终为零。但是,您可以在数值与 None 枚举常量之间执行逻辑(不是按位)比较,以确定数值中是否已设置任何位。
如果创建的是值枚举而不是标志枚举,创建 None 枚举常量仍十分有用。原因是在默认情况下,公共语言运行库会将用于枚举的内存初始化为零。因此,如果不定义值为零的常量,则枚举在创建时将包含非法值。
如果明显存在应用程序需要表示的默认情况,请考虑使用值为零的枚举常量表示默认值。如果不存在默认情况,请考虑使用值为零的枚举常量(这意味着该情况不由任何其他枚举常量表示)。
不要仅为了反映枚举自身的状态而定义枚举值。例如,不要定义仅用于标记枚举末尾的枚举常量。如果需要确定枚举的最后一个值,请显式检查该值。此外,如果枚举常量范围中的所有值都有效,还可以对第一个和最后一个枚举常量执行范围检查。
不要指定保留供将来使用的枚举常量。
在定义采用枚举常量作为值的方法或属性时,应考虑对该值进行验证。原因是即使没有在枚举中定义某个数值,也可以将该数值强制转换为枚举类型。
C#位域主要用于.Net里面对于某一个事物有多种混合状态时使用,单一的枚举更的在事物只具有单一属性时使用。为了更好的实现混合状态,我们可以在枚举加上C#位域标签。下面的这个就是我们在本文中用到的实例:

代码

    位域支持的运算符

    1. “|”:表示两边求并集(元素相加,相同元素只出现一次)

Week week = Week.Tuesday | Week.Monday | Week.Monday;  MessageBox.Show(Convert.ToString(week));     这段代码的结果就是 Monday,Tuesday

    2. “&”:表示两边是否其中一个是另外一个的子集,如果是返回子集,否则返回0(如果其中一个包含另外一个,返回被包含的,否则返回0)

week = Week.Monday & week;  MessageBox.Show(week.ToString());与week = week & Week.Monday;  MessageBox.Show(week.ToString());     上面这两段代码的结果是相同的,如果week的初始值为:Monday,Tuesday,返回的结果为:Monday

    3.“^”:表示从两者的并集中去除两者的交集(把两个的元素合并到一起,如果两个中有公共元素,要将这个公共元素从合并的结果中去除)

week = (Week.Monday | Week.Wednesday)^ (Week.Tuesday | Week.Monday);  MessageBox.Show(week.ToString());  week = (Week.Monday | Week.Wednesday) ^ (Week.Tuesday | Week.Sunday);  MessageBox.Show(week.ToString());     上面两个返回的结果应该为:Tuesday,Wednesday 和 Monday,Tuesday,Wednesday,Sunday

    4.“~”:表示取反,返回的结果我还不知道应该是什么,以后再查一下。用法主要和“&”一起使用,例如:去除其中的某个元素

week = Week.Tuesday | Week.Monday | Week.Wednesday;  week = week &(~Week.Monday);  MessageBox.Show(week.ToString());     上面返回的结果为:Tuesday,Wednesday

    正逆转化

    上面的内容存在数据库时我们可能为了简单只存取数字即可,例如:1表示Monday,3表示Monday,Tuesday。我们可以根据数据库里面的值方便获取存储的内容,代码如下:

week = Week.Monday  | Week.Tuesday;  
MessageBox.Show(Convert.ToString((int)week));  
week = (Week)Enum.Parse(typeof(Week), "10");  
MessageBox.Show(week.ToString());      //返回的结果为:3 和 Tuesday,Tursday
    获取Description标签内容

    我们既然可以给里面的值加上Description,就可以在程序中获取到这个内容,至于用途,大家自己看吧,东西摆出来,大家自己随便怎么用,下面的代码是从网上找到的,内容如下:

代码

Example:


[Flags]
enmu chl{a=1,b=2,c=3}

用flags声明了一个位域,与枚举不同,位域支持不同的&和|操作。
对于普通枚举,|和&操作代表二进制比特操作

enmu ch{a=1,b=2,c=3}

ch.a|ch.c=01|11=3
ch.a&ch.b=01&10=0


对于位域,|和&操作代表逻辑操作
[flags]
enmu chl{a=1,b=2,c=3}
---------|操作把两边位域的元素组合起来(忽略相同部分,并且根据元素的值的和自动转换)


chl.a|chl.b=chl.3 因为1+2=3 这就是位域根据元素和自动转换的例子,但有时这也会给我们带来不便,因为如果chl.c本意不代表chl.a和chl.b的组合,所以我们在设计位域元素值的时候遵循这个原则:从1开始(因为0代表&的无相同元素结果)后一个数是前一个数的2倍,这样就能保证不出现我们本意之外的自动转换

enum chl{a=1,b=2,c=4,d=8}。如果某个元素e希望是其他所有元素的组合(a|b|c|d),我们可以把这个元素的值设为所有元素的和

enum chl{a=1,b=2,c=4,d=8,e=15} (一个位域最好只有一个组合值,那就是all元素)

chl.c|chl.c=chl.c 这是位域忽略相同部分的例子
----------&操作取得两边位域共有的元素,如果没有相同值,返回值0,如果有相同元素,返回相同元素


chl.b|chl.c&chl.b|chl.a=chl.b
chl.b|chl.c&chl.a|chl.d=0
用if(yy&xxx==yy)判断是否存在yy元素
用if(yy&xxx==0)判断是否不存在yy元素

设计位域是遵循条件
1,带[flags]特性
2,元素值从1开始,后面的元素是前面元素值的2倍,最后一个元素设定为all,值为前面元素值的和
3,使用时用|操作组合元素项
4,判断时用   if(位域常量组合A==位域常量组合A&位域变量B)判断位域变量B中是否完全包含位域常量组合A

5, 赋值时用  位域变量=位域变量A&位域变量B


注:位域常量组合A类似 chl.c|chl.b

经常用到的权限管理:


[Flags]

public enum Permission

{

      Select = 1,

      Edit = 2,

    Delete = 4,

    View = 8,   

    All = Select | Edit | Delete | View

   }

 

//可以采用这个函数进行计算:

public static Permission ClearFlag(Permission value, Permission flag)

{       

    value = value & (Permission.All^ flag);

    return value;

}

 

 

//把一个枚举名,或者数字值转换为相应的Enum

Permission value = (Permission)Enum.Parse(typeof(Permission), "7");

 

好了,说了这么多,应该总结一下:

 

一、枚举的优点:
<1>枚举可以使代码更易于维护,有助于确保给变量指定合法的、期望的值。

<2>枚举使代码更清晰,允许用描述性的名称表示整数值,而不是用含义模糊的数来表示。

<3>枚举使代码更易于键入。在给枚举类型的实例赋值时,VS.NET IDE会通过IntelliSense弹出一个包含可接受值的列表框,减少了按键次数,并能够让我们回忆起可能的值

二、枚举和常量,哪个优先考虑:优先考虑枚举。
  在C#中,枚举的真正强大之处是它们在后台会实例化为派生于基类System.Enum的结构。这表示可以对它们调用方法,执行有用的任务。注意因为.NET Framework的执行方式,在语法上把枚举当做结构是不会有性能损失的。实际上,一旦代码编译好,枚举就成为基本类型,与int和float类似。
  但是在实际应用中,你也许会发现,我们经常用英语定义枚举类型,因为开发工具本来就是英文开发的,美国人用起来,就直接能够明白枚举类型的含义。其实,我们在开发的时候就多了一步操作,需要对枚举类型进行翻译。没办法,谁让编程语言是英语写的,如果是汉语写的,那我们也就不用翻译了,用起枚举变得很方便了。举个简单的例子,TimeOfDay.Morning一看到Morning,美国人就知道是上午,但是对于中国的使用者来说,可能有很多人就看不懂,这就需要我们进行翻译、解释,就向上面的getTimeOfDay()的方法,其实就是做了翻译工作。所以,在使用枚举的时候,感觉到并不是很方便,有的时候我们还是比较乐意创建常量,然后在类中,声明一个集合来容纳常量和其意义。
  使用常量定义:这种方法固然可行,但是不能保证传入的参数day就是实际限定的。