枚举
本书迄今介绍的每种类型(除
string外)都有明确的取值范围。诚然,有些类型(如double)的取值范围非常大,可以看成是连续的,但它们仍是一个固定集合。最简单的示例是bool类型,它只能取两个值:true或false。
有时希望变量取的是一个固定集合中的值。例如,让
orientation类型可以存储north、south、east或west中的一个值。
此时可以使用枚举类型。枚举可以完成这个
orientation类型的任务:它们允许定义一个类型,其取值范围是用户提供的值的有限集合。所以,需要创建自己的枚举类型orientation,它可以从上述 4 个值中提取一个值。
注意有一个附加步骤--不是仅仅声明一个给定类型的变量,而是声明和描述一个用户定义的类型,再声明这个新类型的变量。
定义枚举
可以用 enum 关键字来定义枚举,如下所示:
enum <typeName> { <value1>, <value2>, <value3>, ... <valueN> }
接着声明这个新类型的变量:
<typeName> <varName>; 并赋值: <varName> = <typeName>.<value>;
枚举使用一个基本类型来存储。枚举类型可取的每个值都存储为该基本类型的一个值,默认情况下该类型为 int。在枚举声明中添加类型,就可以指定其他基本类型:
enum <typeName> : <underlyingType> { <value1>, <value2>, <value3>, ... <valueN> }
枚举的基本类型可以是 byte、sbyte、short、ushort、int、uint、long 和 ulong。
默认情况下,每个值都会根据定义的顺序(从 0 开始),被自动赋予对应的基本类型值。这意味着 <value1> 的值是 0,<value2> 的值是 1,<value3> 的值是 2 等。可以重写这个赋值过程:使用 = 运算符,指定每个枚举的实际值:
enum <typeName> : <underlyingType> { <value1> = <actualVal1>, <value2> = <actualVal2>, <value3> = <actualVal3>, ... <valueN> = <actualValN> }
还可以使用一个值作为另一个枚举的基础值,为多个枚举指定相同的值:
enum <typeName> : <underlyingType> { <value1> = <actualVal1>, <value2> = <value1>, <value3>, ... <valueN> = <actualValN> }
未赋值的任何值都会自动获得一个初始值,这里使用的值是从比上一个明确声明的值大于 1 开始的序列。例如,在上面的代码中,<value3> 的值是 <value1> + 1。
注意 ⚠️这可能会产生预料不到的问题,在一个定义(如 <value2> = <value1>)后指定的值可能与其他值相同。例如,在下面的代码中,<value4> 的值与 <value2> 相同。
enum <typeName> : <underlyingType> { <value1> = <actualVal1>, <value2>, <value3> = <value1>, <vaue4>, ... <valueN> = <actualValN> }
当然,如果这正是希望的结果,则代码就是正确的。还要注意,以循环方式赋值可能会产生错误,例如:
enum <typeName> : <underlyingType> { <value1> = <value2>, <value2> = <value1>, }
下面看一个示例。其代码定义了一个枚举
orientation,然后演示了它的用法。 把下列代码添加到Program.cs中:
namespace Ch05Ex02 { enum orientation : byte { north = 1, south = 2, east = 3, west = 4 } class Program { static void Main(string[] args) { byte directionByte; string directionString; orientation myDirection = orientation.north; Console.WriteLine("myDirection = {0}", myDirection); directionString = Convert.ToString(myDirection); Console.WriteLine("byte equivalent = {0}", directionByte); Console.WriteLine("string equivalent = {0}", directionString); Console.ReadKey(); } } }
示例的说明 这段代码定义并使用了一个枚举类型
orientation。首先要注意的是,类型定义代码放在名称空间Ch05EX02中,但没有与其余代码放在一起。这是因为在运行期间,定义代码并不是像执行应用程序中的代码那样一行一行地执行。应用程序是从我们熟悉的位置开始执行的,它可以访问新类型,因为该类型位于同一个名称空间中。
这个示例的第一个迭代演示了创建新类型的变量,给它赋值以及把它输出到屏幕上的基本方法。接着修改代码,把枚举值转换为其他类型。注意这里必须使用显式转换。即使
orientation的基本类型是byte,仍必须使用(byte)强制实现类型转换,把myDirection的值转换为byte类型:
directionByte = (byte)myDirection;
如果要将
byte类型转换为orientation,也同样需要进行显式转换。例如,可以使用下述代码将byte变量myByte转换为orientation,并将这个值赋给 myDirection:
myDirection = (orientation)myByte;
当然,这里必须小心,因为并不是所有
byte类型变量的值都可以映射为已定义的orientation值。orientation类型可以存储其他byte值,所以这么做不会直接产生一个错误,但会在应用程序的后面违反逻辑。
要获得枚举的字符串值,可以使用 Convert.ToString():
directionString = Convert.ToString(myDirection);
使用( string )强制类型转换是行不通的,因为需要进行的处理并不仅是把存储在枚举变量中的数据放在 string 变量中,而是更复杂一些。另外,可以使用变量本身的 ToString() 命令。下面的代码与使用 Convert.ToString() 的效果相同:
directionString = myDrection.ToString();
也可以把 string 转换为枚举值,但其语法稍微复杂一些。有一个特定的命令用于完成此类转换,即 Enum.Parse(),其用法如下:
(enumerationType)Enum.Parse(typeof(enumerationType), enumerationValueString);
这里使用了另一个运算符 typeof,它可以得到操作数的类型。对 orientation 类型使用这个命令,如下所示:
string myString = "north"; orientation myDirection = (orientation)Enum.Parse(typeof(orientation), myString);
当然,并非所有字符串值都会映射为一个 orientation 值。如果传送的一个值不能映射为枚举值中的一个,就会产生错误。与C#中的其他值一样,这些值是区分大小写的,所以如果字符串与一个值相同,但大小写不同(例如,myString 设置为 North,而不是 north),就会产生错误。