【.com原稿】枚举是C#中最有趣的部分,大多数开发者只了解其中的一小部分,甚至网上的大部分内容都是教程也只解释了部分枚举。那么,通过这篇文章,我将向大家详细讲解枚举的知识。我将从您所知道的开始,然后再介绍您不知道或知之甚少的内容。一、基础知识枚举是开发者声明的一种值类型,它在编译时声明了一个命名常量值。使用枚举可以使我们的代码易于阅读。我们先来看两个代码段://代码段1voidMethod(intcountry){switch(country){case0://morecodebreak;case1://morecodebreak;case2://morecodebreak;case3://morecodebreak;default://morecodebreak;}}//代码段2voidMethod(Countrycountry){switch(country){caseCountry.CN://morecodebreak;caseCountry.JP://morecodebreak;caseCountry.UK://morecodebreak;caseCountry.USA://morecodebreak;default://morecodebreak;}}从上面两个代码片段我们可以看出两者有明显的区别。我们几乎不知道第一段代码中的case值是什么意思,但是我们在第二段代码中使用了枚举,我们马上就可以知道我们要通过case值表达什么。同样,使用枚举值代替布尔值也可以提高代码的可读性。例如,如果我们要开发一个程序来打开和关闭控制台灯,代码可以写成LightOperating(True),但是我们看不出这种代码能做什么。现在让我们将代码更改为LightOperating(Light.On)。修改代码后,很容易看出你想表达什么。1.枚举的定义和取值枚举的定义有两种方式,一种是普通方式,一种是自定义方式。无论使用哪种方式,都需要使用关键字enum来标识该类型为枚举类型,枚举值实现为整型常量。我们来看看这两种方式是如何定义枚举的。common方式就是我们经常使用的方式,也是默认的方式。这个方法很简单,代码如下:enumCountry{CN,UK,JP,USA}在上面的代码段中我们定义了一个国家枚举,第一个枚举值对应的整数常量为0,第二个枚举的整数枚举值对应的常量为1,以此类推,后面的枚举值对应的整型常量分别为2和3。但在某些情况下,我们需要自定义枚举值对应的整型常量。这时候,我们就需要使用自定义的方法。自定义方法也称为枚举值的显式赋值。它的方法如下:enumCountry{CN=3,UK,JP=70,USA=67}我们将第一个枚举值对应的整数常量设置为3。此时,第二个枚举值的整数常量不是1,而是4,因为枚举值没有被赋值的时候,它会把上一个枚举值对应的整数值加1作为自己对应的整数值。最后两个枚举值是显式赋值的,所以对应的整型值就是赋值。枚举的值也很简单,就是枚举名称。枚举值,例如Country.UK。提示:这里有一些建议:枚举值的名称不要包含枚举名称;枚举名称应以单数形式出现(属性除外)。2.枚举的类型至此,我们已经定义了枚举类型使用的基本类型int类型,但是枚举不仅可以使用int类型,还可以使用除char类型以外的所有基本类型。我们可以使用继承语法来指定其他类型。enumCountry:short{CN=3,UK,JP=70,USA=67}在上面的代码中,我们明确定义了枚举使用的基本类型为short。这里虽然使用了继承语法,但是并没有建立继承关系。所有枚举基类都是System.Enum。这些类是密封类,不能从现有枚举类型派生新成员。对于枚举类型的变量,取值不限于声明中命名的值,可以先将值转换为底层类型,再转换为枚举类型。之所以这样设计,是因为在不破坏旧版本的情况下,在未来的API中很有可能为枚举添加新的值。但是这也有一个缺陷,枚举允许在运行时赋值未知值,我们在开发时需要考虑到这一点。并且后面在枚举中添加新的枚举值时,应该添加在所有枚举值之后,或者显示指定枚举值对应的值,避免因为添加新值而导致枚举类型中的枚举值对应值的变化。提示:在开发中,我们应该尽量使用int作为枚举的基类型,除非出于性能问题或互操作性考虑而考虑使用更小的类型。2.枚举转换枚举转换主要涉及枚举与枚举的转换,枚举与数字与字符串的转换。1.枚举之间的转换首先要说明的是,C#中不支持不同枚举数组之间的直接转换,所以如果我们想实现不同枚举数组之间的转换,可以利用CLR的松散赋值兼容性来实现这个特性要转换,需要转换的两个枚举必须具有相同的基础类型。同样,我们通过一个例子来看看具体的实现方法。staticvoidMain(string[]args){CountryAllName[]can=(CountryAllName[])(Array)newCountry[4];}enumCountry{CN,UK,JP,USA}enumCountryAllName{China,UnitedKingdom,Japan,UnitedStates}正在使用这种方法使用时可能会出现意想不到的错误或结果,相关开发规范也没有说这种方法每次都有效,所以我不推荐使用,除非是一些极端的场景。2.枚举与字符串的转换要将枚举转换为字符串,可以直接使用ToString()方法。在枚举值ToString之后,会直接输出枚举值标识符的字符串形式,比如Country.CN.ToString()得到的结果就是字符串CN。当然你也可以使用Enum.GetNames和Enum.GetName方法来获取。下面我简单介绍一下这两种方法的使用。GetNamesGetNames方法需要传入一个枚举类型,返回值是一个字符串数组。比如需要获取Country的第二个国家,可以这样写Enum.GetNames(typeof(Country))[1],返回结果为UK。GetNameGetName方法返回一个字符串,是需要获取的指定枚举值的字符串形式。同样,我们得到第二个国家,Enum.GetName(typeof(Country),1),返回值也是UK。将字符串转换为枚举也很简单。它还使用Enum基类的静态方法Parse。比如我们可以将JP转换为枚举Country(国家)Enum.Parse(typeof(Country),"JP")的枚举值。这里要注意一点,TryParse方法只出现在.net4.0中,所以在.net4.0以下的版本中如果要将字符串转换为枚举,需要进行适当的错误处理,防止字符串不存在而枚举类型中的枚举值。提示:字符串到枚举的转换不能本地化。如果一定要本地化,那一定是那些上层用户看不到的消息。因此,在实际开发中,应尽量避免枚举与字符串之间的转换。3.枚举与数字的转换我们可以使用强制将枚举转换为数字,例如(int)Country.CN返回0。我们有两种方式将数字转换为枚举,一种是使用强制转换,另一种是使用强制转换。使用Enum的静态方法发送ToObject。强制比较简单,Countrycountry=(Country)2ToObjectToObject方法需要传入枚举类型和要转换的数字,例如Countrycountry=(Country)Enum.ToObject(typeof(Country),2)4.注意字符串转枚举和数字转枚举都要先判断要转换的值是否包含在枚举中。判断的方法也很简单。你只需要调用Enum的静态方法IsDefined即可。比如我要将0和HK转换为枚举,代码如下:Typetype=typeof(Country);if(Enum.IsDefined(type,0)){Enum.ToObject(type,0);}if(Enum.IsDefined(type,"HK")){Enum.Parse(typeof(Country),"HK");}上面代码中只有0会成功转换为枚举值CN,因为0对应的枚举值为CN,HK不在列举之列。3.标志和属性在这一节中,让我们解释一下标志和属性。flags和attributes在开发中用的比较少,大部分程序员都不太了解。1.Flags在开发中,有时我们希望结合使用枚举来表示复合值。然后我们需要定义标志枚举。标志枚举的名称是复数形式,表示标志的集合。一般我们会使用按位或运算符来链接枚举值,并使用HasFlags方法或按位与运算符来判断特定位是否存在。比较经典的标志枚举是位于System.IO命名空间的FileAttributes标志枚举,它列出了文件的所有属性,比如只读、隐藏、磁盘位置等,以及它包含的所有枚举值可以是彼此的组合,比如一个文件既是隐藏的又是只读的。flag枚举的定义方式如下:[Flags]enumWeekDays{Monday=1,Tuesday=2,Wednesday=4,Thursday=8,Friday=16,Saturday=32,Sunday=64}在上面的代码中你会找到一个规律,每个枚举值对应的整数值都是2的n次方,这是为什么呢。在标志枚举中,要求多个枚举值相互组合的结果不能包含在标志枚举中,根据位运算的特点,可以方便的使用位运算符来计算一个枚举是否value包含另一个Enumeration值,这在权限系统中非常有用。2.属性也可以用在属性枚举值上。比如我们需要打印出枚举值的中文名称,可以通过属性的形式来设置。首先,我们需要定义一个属性:publicclassEnumChineseAttribute:Attribute{privatestringm_strDescription;publicEnumChineseAttribute(stringchineseName){m_strDescription=chineseName;}publicstringDescription{get{returnm_strDescription;}}}enumCountry{[EnumChinese("China")]CN,[EnumChinese("UK")]UK,[EnumChinese("Japan")]JP,[EnumChinese("UnitedStates")]USA}staticvoidMain(string[]args){Countrycountry=Country.CN;FieldInfofieldInfo=country.GetType().GetField("CN");object[]attribArray=fieldInfo.GetCustomAttributes(false);EnumChineseAttributeattrib=(EnumChineseAttribute)attribArray[0];Console.WriteLine(attrib.Description);Console.Read();}通过上面的代码,我们可以得到CN对应的中文名称。此代码尚未进一步优化,必须在实际项目中进行打包和优化。4.小结本文主要讲解枚举相关的知识。内容有点琐碎,但在实际开发中还是蛮实用的。文中我提到的要点和规定都在实际开发中得到了验证,读者可以直接使用。作者简介:朱刚,化名苗叔,国内某技术博客认证专家,.NET高级开发工程师。曾就职于初创公司,从事企业级安全监控系统开发。【原创稿件,合作网站转载请注明原作者和出处为.com】
