枚举

  本书迄今介绍的每种类型(除 string 外)都有明确的取值范围。诚然,有些类型(如 double)的取值范围非常大,可以看成是连续的,但它们仍是一个固定集合。最简单的示例是 bool 类型,它只能取两个值:truefalse。  有时希望变量取的是一个固定集合中的值。例如,让 orientation 类型可以存储 northsoutheastwest 中的一个值。  此时可以使用枚举类型。枚举可以完成这个 orientation 类型的任务:它们允许定义一个类型,其取值范围是用户提供的值的有限集合。所以,需要创建自己的枚举类型 orientation,它可以从上述 4 个值中提取一个值。  注意有一个附加步骤—不是仅仅声明一个给定类型的变量,而是声明和描述一个用户定义的类型,再声明这个新类型的变量。

定义枚举

  可以用 enum 关键字来定义枚举,如下所示:

  1. enum <typeName>
  2. {
  3. <value1>,
  4. <value2>,
  5. <value3>,
  6. ...
  7. <valueN>
  8. }

  接着声明这个新类型的变量:

  1. <typeName> <varName>;
  2. 并赋值:
  3. <varName> = <typeName>.<value>;

  枚举使用一个基本类型来存储。枚举类型可取的每个值都存储为该基本类型的一个值,默认情况下该类型为 int。在枚举声明中添加类型,就可以指定其他基本类型:

  1. enum <typeName> : <underlyingType>
  2. {
  3. <value1>,
  4. <value2>,
  5. <value3>,
  6. ...
  7. <valueN>
  8. }

  枚举的基本类型可以是 bytesbyteshortushortintuintlongulong

  默认情况下,每个值都会根据定义的顺序(从 0 开始),被自动赋予对应的基本类型值。这意味着 <value1> 的值是 0,<value2> 的值是 1,<value3> 的值是 2 等。可以重写这个赋值过程:使用 = 运算符,指定每个枚举的实际值:

  1. enum <typeName> : <underlyingType>
  2. {
  3. <value1> = <actualVal1>,
  4. <value2> = <actualVal2>,
  5. <value3> = <actualVal3>,
  6. ...
  7. <valueN> = <actualValN>
  8. }

  还可以使用一个值作为另一个枚举的基础值,为多个枚举指定相同的值:

  1. enum <typeName> : <underlyingType>
  2. {
  3. <value1> = <actualVal1>,
  4. <value2> = <value1>,
  5. <value3>,
  6. ...
  7. <valueN> = <actualValN>
  8. }

  未赋值的任何值都会自动获得一个初始值,这里使用的值是从比上一个明确声明的值大于 1 开始的序列。例如,在上面的代码中,<value3> 的值是 <value1> + 1

  注意 ⚠️这可能会产生预料不到的问题,在一个定义(如 <value2> = <value1>)后指定的值可能与其他值相同。例如,在下面的代码中,<value4> 的值与 <value2> 相同。

  1. enum <typeName> : <underlyingType>
  2. {
  3. <value1> = <actualVal1>,
  4. <value2>,
  5. <value3> = <value1>,
  6. <vaue4>,
  7. ...
  8. <valueN> = <actualValN>
  9. }

  当然,如果这正是希望的结果,则代码就是正确的。还要注意,以循环方式赋值可能会产生错误,例如:

  1. enum <typeName> : <underlyingType>
  2. {
  3. <value1> = <value2>,
  4. <value2> = <value1>,
  5. }
下面看一个示例。其代码定义了一个枚举 orientation,然后演示了它的用法。把下列代码添加到 Program.cs 中:

  1. 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 类型:

  1. directionByte = (byte)myDirection;


  如果要将 byte 类型转换为 orientation,也同样需要进行显式转换。例如,可以使用下述代码将 byte 变量 myByte 转换为 orientation,并将这个值赋给 myDirection:

  1. myDirection = (orientation)myByte;


  当然,这里必须小心,因为并不是所有 byte 类型变量的值都可以映射为已定义的 orientation 值。orientation 类型可以存储其他 byte 值,所以这么做不会直接产生一个错误,但会在应用程序的后面违反逻辑。

  要获得枚举的字符串值,可以使用 Convert.ToString():

  1. directionString = Convert.ToString(myDirection);

  使用( string )强制类型转换是行不通的,因为需要进行的处理并不仅是把存储在枚举变量中的数据放在 string 变量中,而是更复杂一些。另外,可以使用变量本身的 ToString() 命令。下面的代码与使用 Convert.ToString() 的效果相同:

  1. directionString = myDrection.ToString();

  也可以把 string 转换为枚举值,但其语法稍微复杂一些。有一个特定的命令用于完成此类转换,即 Enum.Parse(),其用法如下:

  1. (enumerationType)Enum.Parse(typeof(enumerationType), enumerationValueString);

  这里使用了另一个运算符 typeof,它可以得到操作数的类型。对 orientation 类型使用这个命令,如下所示:

  1. string myString = "north";
  2. orientation myDirection = (orientation)Enum.Parse(typeof(orientation), myString);

  当然,并非所有字符串值都会映射为一个 orientation 值。如果传送的一个值不能映射为枚举值中的一个,就会产生错误。与C#中的其他值一样,这些值是区分大小写的,所以如果字符串与一个值相同,但大小写不同(例如,myString 设置为 North,而不是 north),就会产生错误。