Dart 枚举与符号
枚举(enum)用于定义一组有名字的常量值,让代码更具可读性。
本章介绍 Dart 枚举的定义方式、增强枚举(带方法和属性)以及 Symbol 和 Rune 类型。
enum 枚举定义
枚举是一种特殊的类,用于表示固定数量的常量值。
最典型的场景是表示状态、方向、颜色等有限选项。
实例
// 定义枚举
enum Status {
pending, // 待处理
approved, // 已批准
rejected, // 已拒绝
cancelled, // 已取消
}
void main() {
// 使用枚举值
Status currentStatus = Status.pending;
print('当前状态: $currentStatus');
// 通过 .index 获取索引(从 0 开始)
print('索引值: ${currentStatus.index}'); // 0
// 通过 .values 获取所有枚举值
print('所有状态: ${Status.values}');
// 遍历所有枚举值
for (var status in Status.values) {
print('RUNOOB 状态: $status, 索引: ${status.index}');
}
// 通过字符串获取枚举值
Status parsed = Status.values.byName('approved');
print('解析出的状态: $parsed');
// 在 switch 中使用枚举
switch (currentStatus) {
case Status.pending:
print('订单待处理');
break;
case Status.approved:
print('订单已批准');
break;
case Status.rejected:
print('订单被拒绝');
break;
case Status.cancelled:
print('订单已取消');
break;
}
}
enum Status {
pending, // 待处理
approved, // 已批准
rejected, // 已拒绝
cancelled, // 已取消
}
void main() {
// 使用枚举值
Status currentStatus = Status.pending;
print('当前状态: $currentStatus');
// 通过 .index 获取索引(从 0 开始)
print('索引值: ${currentStatus.index}'); // 0
// 通过 .values 获取所有枚举值
print('所有状态: ${Status.values}');
// 遍历所有枚举值
for (var status in Status.values) {
print('RUNOOB 状态: $status, 索引: ${status.index}');
}
// 通过字符串获取枚举值
Status parsed = Status.values.byName('approved');
print('解析出的状态: $parsed');
// 在 switch 中使用枚举
switch (currentStatus) {
case Status.pending:
print('订单待处理');
break;
case Status.approved:
print('订单已批准');
break;
case Status.rejected:
print('订单被拒绝');
break;
case Status.cancelled:
print('订单已取消');
break;
}
}
当前状态: Status.pending 索引值: 0 所有状态: [Status.pending, Status.approved, Status.rejected, Status.cancelled] RUNOOB 状态: Status.pending, 索引: 0 RUNOOB 状态: Status.approved, 索引: 1 RUNOOB 状态: Status.rejected, 索引: 2 RUNOOB 状态: Status.cancelled, 索引: 3 解析出的状态: Status.approved 订单待处理
枚举和 switch 是绝配。当你在 switch 中处理所有枚举值时,Dart 编译器会检查你是否覆盖了所有情况。这能避免遗漏分支导致的 bug。
增强枚举(带方法和属性)
Dart 3.0 引入了增强枚举(Enhanced Enums),允许枚举拥有字段、方法和构造函数。
实例
// 增强枚举:可以拥有成员变量、构造函数和方法
enum HttpStatus {
// 枚举值通过构造函数传递参数
ok(200, '请求成功'),
created(201, '资源已创建'),
badRequest(400, '请求错误'),
unauthorized(401, '未授权'),
notFound(404, '资源未找到'),
serverError(500, '服务器内部错误');
// 成员变量:每个枚举值都有 code 和 message
final int code;
final String message;
// 构造函数:必须是 const
const HttpStatus(this.code, this.message);
// 枚举方法
bool get isSuccess => code >= 200 && code < 300;
bool get isClientError => code >= 400 && code < 500;
bool get isServerError => code >= 500;
// 静态方法:根据状态码查找枚举
static HttpStatus? fromCode(int code) {
try {
return HttpStatus.values.firstWhere((s) => s.code == code);
} catch (_) {
return null;
}
}
}
void main() {
var status = HttpStatus.notFound;
print('RUNOOB HTTP 状态: $status');
print('状态码: ${status.code}');
print('消息: ${status.message}');
print('是否成功: ${status.isSuccess}');
print('是否客户端错误: ${status.isClientError}');
// 使用静态方法查找
var found = HttpStatus.fromCode(201);
if (found != null) {
print('找到状态: ${found.message}');
}
var unknown = HttpStatus.fromCode(999);
print('未知状态码: $unknown');
}
enum HttpStatus {
// 枚举值通过构造函数传递参数
ok(200, '请求成功'),
created(201, '资源已创建'),
badRequest(400, '请求错误'),
unauthorized(401, '未授权'),
notFound(404, '资源未找到'),
serverError(500, '服务器内部错误');
// 成员变量:每个枚举值都有 code 和 message
final int code;
final String message;
// 构造函数:必须是 const
const HttpStatus(this.code, this.message);
// 枚举方法
bool get isSuccess => code >= 200 && code < 300;
bool get isClientError => code >= 400 && code < 500;
bool get isServerError => code >= 500;
// 静态方法:根据状态码查找枚举
static HttpStatus? fromCode(int code) {
try {
return HttpStatus.values.firstWhere((s) => s.code == code);
} catch (_) {
return null;
}
}
}
void main() {
var status = HttpStatus.notFound;
print('RUNOOB HTTP 状态: $status');
print('状态码: ${status.code}');
print('消息: ${status.message}');
print('是否成功: ${status.isSuccess}');
print('是否客户端错误: ${status.isClientError}');
// 使用静态方法查找
var found = HttpStatus.fromCode(201);
if (found != null) {
print('找到状态: ${found.message}');
}
var unknown = HttpStatus.fromCode(999);
print('未知状态码: $unknown');
}
RUNOOB HTTP 状态: HttpStatus.notFound 状态码: 404 消息: 资源未找到 是否成功: false 是否客户端错误: true 找到状态: 资源已创建 未知状态码: null
增强枚举让枚举从"命名常量集合"升级为"有行为的类型",可以封装与该枚举相关的逻辑。
增强枚举的构造函数必须是 const,成员变量必须是 final。这是因为枚举值本身就是编译时常量。
Symbol 类型
Symbol 代表 Dart 程序中的标识符(变量名、函数名等)。
它在日常开发中不常用,主要在反射(Reflection)和代码生成场景中使用。
实例
void main() {
// 使用 # 前缀创建 Symbol
Symbol sym1 = #runoob;
Symbol sym2 = #helloWorld;
print('Symbol 1: $sym1'); // Symbol("runoob")
print('Symbol 2: $sym2'); // Symbol("helloWorld")
// Symbol 比较
Symbol sym3 = #runoob;
print('sym1 == sym3: ${sym1 == sym3}'); // true
// Symbol 可以用作 Map 的键
var metadata = {
#author: 'RUNOOB',
#version: '1.0.0',
#description: 'Dart 教程',
};
print('作者: ${metadata[#author]}');
}
// 使用 # 前缀创建 Symbol
Symbol sym1 = #runoob;
Symbol sym2 = #helloWorld;
print('Symbol 1: $sym1'); // Symbol("runoob")
print('Symbol 2: $sym2'); // Symbol("helloWorld")
// Symbol 比较
Symbol sym3 = #runoob;
print('sym1 == sym3: ${sym1 == sym3}'); // true
// Symbol 可以用作 Map 的键
var metadata = {
#author: 'RUNOOB',
#version: '1.0.0',
#description: 'Dart 教程',
};
print('作者: ${metadata[#author]}');
}
Symbol 1: Symbol("runoob")
Symbol 2: Symbol("helloWorld")
sym1 == sym3: true
作者: RUNOOB
Symbol 在 Dart 编译后通常会被压缩(minified),所以不要依赖 Symbol("runoob").toString() 的结果来做逻辑判断。Symbol 的正确用法是通过 dart:mirrors 库进行反射操作。
Rune 与 Unicode
Rune 是 Dart 中表示 Unicode 码点的类型。
在 Dart 中,字符串是 UTF-16 编码的序列。
对于基本多语言平面(BMP)之外的字符(如 emoji),需要使用 Rune 来处理。
实例
void main() {
// 获取字符串的 Unicode 码点
String text = 'RUNOOB';
print('字符: $text');
print('码点列表: ${text.runes.toList()}');
// 遍历每个字符和它的码点
for (var char in text.runes) {
print('${String.fromCharCode(char)} -> U+${char.toRadixString(16).toUpperCase().padLeft(4, '0')}');
}
// 从码点创建字符
var heart = String.fromCharCode(0x2764);
print('心形: $heart');
// 处理多码点字符(如某些 emoji)
var smile = '\u{1F600}'; // 使用 \u{} 语法表示超出 BMP 的字符
print('笑脸: $smile');
// 获取多码点字符的码点
print('笑脸码点: ${smile.runes.map((r) => 'U+${r.toRadixString(16).toUpperCase()}').toList()}');
}
// 获取字符串的 Unicode 码点
String text = 'RUNOOB';
print('字符: $text');
print('码点列表: ${text.runes.toList()}');
// 遍历每个字符和它的码点
for (var char in text.runes) {
print('${String.fromCharCode(char)} -> U+${char.toRadixString(16).toUpperCase().padLeft(4, '0')}');
}
// 从码点创建字符
var heart = String.fromCharCode(0x2764);
print('心形: $heart');
// 处理多码点字符(如某些 emoji)
var smile = '\u{1F600}'; // 使用 \u{} 语法表示超出 BMP 的字符
print('笑脸: $smile');
// 获取多码点字符的码点
print('笑脸码点: ${smile.runes.map((r) => 'U+${r.toRadixString(16).toUpperCase()}').toList()}');
}
字符: RUNOOB 码点列表: [82, 85, 78, 79, 79, 66] R -> U+0052 U -> U+0055 N -> U+004E O -> U+004F O -> U+004F B -> U+0042 心形: ❤ 笑脸: 😀 笑脸码点: [U+1F600]
大部分日常开发中你不需要直接操作 Rune,字符串本身已经能很好地处理 Unicode。
Rune 主要在需要精确操作字符码点时使用,比如文本编辑器、字体渲染等底层场景。
