枚举

枚举(Enum)是一种用于表示固定数量的常量值的特殊类(class),在第1章数据类型一节中已经简单介绍过,文本将更详细地讨论它。

简单枚举

使用关键字enum声明一个简单枚举如下:

// ex14d.dart
enum Color { red, green, blue }

void main() {
  print(Color.red); // Output: Color.red
  print(Color.blue.name); // Output: blue
  print(Color.blue.index); // Output: 2
}

每个枚举值都有个与之关联的数字,称之为index,该数字从0开始。此例中 red, green, blueindex 分别为0、1、2,可见index是按枚举值的声明顺序依次分配的。

详尽性检查

当枚举值用在switch语句或表达式时,Dart编译器将进行详尽性检查(完备性检查),迫使程序员处理所有的枚举值。

// ex471.dart
enum Color { red, green, blue }

String example(Color color) => switch (color) {
  Color.red => 'Red apple',
  Color.green => 'Green tree',
  Color.blue => 'Blue sky',
};

void main() {
  print(example(Color.blue)); // Output: Blue sky
}

下面这段代码无法通过编译,因为 Color.blue 没有得到处理。

// ex472.dart
enum Color { red, green, blue }

String example(Color color) => switch (color) {
  Color.red => 'Red apple',
  Color.green => 'Green tree',
};

可使用 wildcard (即通配符 _)匹配剩余的枚举值。

// ex473.dart
enum Color { red, green, blue, orange, pink }

String example(Color color) => switch (color) {
  Color.red => 'Red apple',
  Color.green => 'Green tree',
  _ => 'Whatever',
};

void main() {
  print(example(Color.blue)); // Output: Whatever
}

增强型枚举

枚举是一种特殊的类,它可以有自己的字段、方法和常量构造器,这便是增强型枚举的概念(自Dart 2.17开始引入)。

// ex474.dart
enum Status {
  // 1
  continue_(100, message: 'Continue'),
  ok(200, message: 'OK'),
  movedPermanently(301, message: 'Moved Permanently'),
  notFound(404, message: 'Not Found'),
  badGateway(502, message: 'Bad Gateway');

  final int code; // 2
  final String message; // 2a
  const Status(this.code, {required this.message}); // 3

  static Status of(int code) => // 4
      values.firstWhere((status) => status.code == code);

  bool get is2xx => (code ~/ 100) == 2; // 5

  bool get isOk => this == ok; // 5a

  @override
  String toString() => '$message($code)'; // 6
}

void main() {
  print(Status.values); // 7
  // Output: [Continue(100), OK(200), Moved Permanently(301), Not Found(404), Bad Gateway(502)]

  print(Status.ok); // Output: OK(200)
  print(Status.of(404)); // Output: Not Found(404)
  print(Status.ok.isOk); // Output: true
  print(Status.of(502).is2xx); // Output: false

  Status.of(10);
  // Unhandled exception:
  // Bad state: No element
}
  1. Status是一个枚举,首先列出它所有可能的取值 continue_ok等,这些枚举值是常量对象,通过调用常量构造器得到,例如 ok(200, message: 'OK');
  2. codemessageStatus的实例变量,它们都必须声明为final
  3. Status的常量构造器,code是位置参数,message是命名参数;
  4. of是一个静态方法,将参数code转换为枚举值,此方法可以改为factory构造器;
  factory Status.of(int code) => // 4
      values.firstWhere((status) => status.code == code);
  1. is2xxisOk是两个自定义的getter;
  2. 重写toString()方法;
  3. Status.values是由Dart运行时(runtime)自动创建的getter,返回包含Status所有枚举值的一个列表(List<Status>), 其元素的顺序与它们被声明的顺序一致。

增强型枚举与普通类有着类似的语法,但有如下限制:

  • 实例变量必须是 final 的,包括通过 mixin 添加的变量;
  • 所有的生成构造函数必须是常量构造器,即用 const 进行修饰;
  • 不能继承其它类,因为它自动继承了 Enum
  • 不能重写 indexhashCode和 运算符 ==;
  • 不能声明名为 values 的成员,因为它会与自动生成的values getter 冲突;
  • 枚举的所有实例都必须在开头声明,并且至少得声明一个实例。

这些限制听起来都比较合理,也不难理解,不再赘述。

参考资料