19 枚举

19.1 常规

枚举类型是声明一组命名常量的非重复值类型(§8.3)。

示例: 示例

enum Color
{
    Red,
    Green,
    Blue
}

声明一个名称为 Color 成员 Red的枚举类型, Green以及 Blue

end 示例

19.2 枚举声明

枚举声明声明新的枚举类型。 枚举声明以关键字 enum开头,并定义枚举的名称、辅助功能、基础类型和成员。

enum_declaration
    : attributes? enum_modifier* 'enum' identifier enum_base? enum_body ';'?
    ;

enum_base
    : ':' integral_type
    | ':' integral_type_name
    ;

integral_type_name
    : type_name // Shall resolve to an integral type other than char
    ;

enum_body
    : '{' enum_member_declarations? '}'
    | '{' enum_member_declarations ',' '}'
    ;

每个枚举类型都有一个相应的整数类型,称为枚举类型的基础类型 此基础类型应能够表示枚举中定义的所有枚举器值。 如果存在enum_base,则显式声明基础类型。 基础类型应为非整型类型之一§8.3.6)。char 基础类型可由 integral_type§8.3.5) 或 . integral_type_name. 解析 integral_type_name 方式与 type_name§7.8.1) 相同,包括考虑任何 using 指令(§14.5)。

注意:该 char 类型不能用作基础类型,无论是关键字还是通过 integral_type_nameend note

未显式声明基础类型的枚举声明具有基础类型 int

示例:示例

enum Color : long
{
    Red,
    Green,
    Blue
}

声明具有基础类型的 long枚举。

end 示例

注意:开发人员可以选择使用基础类型的基础类型 long,如示例中所示,以允许使用值,这些值位于范围 long 中,但不在范围 int中,或保留此选项供将来使用。 end note

注意:C# 允许enum_body中的尾随逗号,就像它允许array_initializer中的一个 (§17.7) 一样。 end note

枚举声明不能包含类型参数列表,但泛型类声明或泛型结构声明内嵌套的任何枚举都是泛型枚举声明,因为应提供包含类型的类型参数来创建构造类型(§8.4)。

19.3 枚举修饰符

enum_declaration可以选择包括枚举修饰符序列:

enum_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    ;

同一修饰符在枚举声明中多次出现编译时错误。

枚举声明的修饰符与类声明(§15.2.2.2)的含义相同。 但是, abstract枚举声明中不允许使用和 sealed修饰 static 符。 枚举不能是抽象的,不允许派生。

19.4 枚举成员

枚举类型声明的正文定义零个或多个枚举成员,这些成员是枚举类型的命名常量。 没有两个枚举成员可以具有相同的名称。

enum_member_declarations
    : enum_member_declaration (',' enum_member_declaration)*
    ;
enum_member_declaration
    : attributes? identifier ('=' constant_expression)?
    ;

每个枚举成员都有一个关联的常量值。 此值的类型是包含枚举的基础类型。 每个枚举成员的常量值应位于枚举的基础类型范围内。

示例:示例

enum Color: uint
{
    Red = -1,
    Green = -2,
    Blue = -3
}

导致编译时错误,因为常量值-1-2,并且-3不在基础整型的范围uint中。

end 示例

多个枚举成员可以共享相同的关联值。

示例:示例

enum Color
{
    Red,
    Green,
    Blue,
    Max = Blue
}

显示一个枚举,Blue 其中两个枚举成员以及 Max具有相同的关联值。

end 示例

枚举成员的关联值是隐式分配的,也可以是显式分配的。 如果枚举成员的声明具有 constant_expression 初始值设定项,则该常量表达式的值(隐式转换为枚举的基础类型)是枚举成员的关联值。 如果枚举成员的声明没有初始值设定项,则隐式设置其关联值,如下所示:

  • 如果枚举成员是枚举类型中声明的第一个枚举成员,则其关联值为零。
  • 否则,枚举成员的关联值是通过逐个增加文本前面的枚举成员的关联值来获取的。 此增加的值应位于可由基础类型表示的值范围内,否则会发生编译时错误。

示例:示例

enum Color
{
    Red,
    Green = 10,
    Blue
}

class Test
{
    static void Main()
    {
        Console.WriteLine(StringFromColor(Color.Red));
        Console.WriteLine(StringFromColor(Color.Green));
        Console.WriteLine(StringFromColor(Color.Blue));
    }

    static string StringFromColor(Color c)
    {
        switch (c)
        {
            case Color.Red:
                return $"Red = {(int) c}";
            case Color.Green:
                return $"Green = {(int) c}";
            case Color.Blue:
                return $"Blue = {(int) c}";
            default:
                return "Invalid color";
      }
   }
}

输出枚举成员名称及其关联值。 输出为:

Red = 0
Green = 10
Blue = 11

原因如下:

  • 枚举成员 Red 会自动分配值零(因为它没有初始值设定项,并且是第一个枚举成员):
  • 枚举成员 Green 显式给定值 10;
  • 和枚举成员 Blue 会自动分配大于其前面文本的成员的值。

end 示例

枚举成员的关联值不得直接或间接地使用其自己的关联枚举成员的值。 除此循环限制外,枚举成员初始值设定项可以自由引用其他枚举成员初始值设定项,而不考虑其文本位置。 在枚举成员初始值设定项中,其他枚举成员的值始终被视为具有其基础类型的类型,以便在引用其他枚举成员时不需要强制转换。

示例:示例

enum Circular
{
    A = B,
    B
}

导致编译时错误,因为 A 声明和声明是 B 循环的。 A 依赖于 B 显式,并 B 隐式依赖 A

end 示例

枚举成员的命名和作用域与类中的字段完全相同。 枚举成员的范围是其包含枚举类型的正文。 在该范围内,枚举成员可以通过其简单名称来引用。 从所有其他代码中,枚举成员的名称应使用其枚举类型的名称进行限定。 枚举成员没有任何声明的可访问性 - 如果枚举成员包含枚举类型可访问,则其可访问。

19.5 System.Enum 类型

该类型 System.Enum 是所有枚举类型的抽象基类(这不同于枚举类型的基础类型),继承 System.Enum 的成员在任何枚举类型中都可用。 从任何枚举类型到任何枚举类型都存在装箱转换(§10.2.9),并且从任何枚举类型存在System.Enum取消装箱转换(§10.3.7)。System.Enum

请注意,System.Enum这不是enum_type。 相反,这是派生所有enum_type class_type。 该类型 System.Enum 继承自类型 System.ValueType§8.3.2),而类型又继承自类型 object。 在运行时,类型 System.Enum 值可以是 null 或引用任何枚举类型的装箱值。

19.6 枚举值和操作

每个枚举类型定义不同的类型;显式枚举转换(§10.3.3)需要在枚举类型和整型之间转换,或者在两个枚举类型之间转换。 枚举类型的值集与基础类型的值集相同,不限于命名常量的值集。 枚举的基础类型的任何值都可以强制转换为枚举类型,并且是该枚举类型的非重复有效值。

枚举成员具有其包含枚举类型的类型(其他枚举成员初始值设定项除外:请参阅 §19.4)。 枚举类型 E 中声明的枚举成员的值与关联的值 v(E)v

以下运算符可用于枚举类型的值:

每个枚举类型都自动派生自该类 System.Enum (反过来又派生自 System.ValueTypeobject)。 因此,此类的继承方法和属性可用于枚举类型的值。