枚举

  本书迄今介绍的每种类型(除 string 外)都有明确的取值范围。诚然,有些类型(如 double)的取值范围非常大,可以看成是连续的,但它们仍是一个固定集合。最简单的示例是 bool 类型,它只能取两个值:truefalse

  有时希望变量取的是一个固定集合中的值。例如,让 orientation 类型可以存储 northsoutheastwest 中的一个值。

  此时可以使用枚举类型。枚举可以完成这个 orientation 类型的任务:它们允许定义一个类型,其取值范围是用户提供的值的有限集合。所以,需要创建自己的枚举类型 orientation,它可以从上述 4 个值中提取一个值。

  注意有一个附加步骤--不是仅仅声明一个给定类型的变量,而是声明和描述一个用户定义的类型,再声明这个新类型的变量。

定义枚举

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

        enum <typeName>
        {
            <value1>,
            <value2>,
            <value3>,
            ...
            <valueN>
        }

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

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

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

        enum <typeName> : <underlyingType>
        {
            <value1>,
            <value2>,
            <value3>,
            ...
            <valueN>
        }

  枚举的基本类型可以是 bytesbyteshortushortintuintlongulong

  默认情况下,每个值都会根据定义的顺序(从 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),就会产生错误。

🔚