一、枚举不只是简单的值容器

很多人刚开始用Dart枚举时,只是把它当作一个简单的值容器。比如这样:

enum HttpStatus {
  success,
  notFound,
  serverError
}

这确实能用,但就像只用了手机的通话功能一样浪费。Dart枚举实际上是个隐藏的瑞士军刀,今天我们就来解锁它的高级玩法。

来看个更有意思的例子:

enum NetworkResponse with LoggerMixin {
  success(200, 'OK') {
    // 可以给特定枚举值添加方法
    void cacheResponse() => print('Caching successful response');
  },
  
  notFound(404, 'Not Found'),
  
  serverError(500, 'Internal Server Error');

  final int code;
  final String description;
  
  const NetworkResponse(this.code, this.description);
  
  // 公共方法
  void log() => print('$code: $description');
}

看到没?枚举可以:

  • 有构造函数和属性
  • 实现mixin
  • 为特定值添加专属方法
  • 定义公共方法

二、模式匹配:枚举的真正威力

Dart 3.0引入的模式匹配让枚举真正起飞了。来看个电商订单状态的例子:

enum OrderStatus {
  pending,
  processing,
  shipped,
  delivered,
  cancelled;

  // 用switch表达式处理不同状态
  String get statusMessage => switch(this) {
    pending => '您的订单已接收',
    processing => '正在准备发货',
    shipped => '已发货,物流更新中',
    delivered => '订单已完成',
    cancelled => '订单已取消',
  };
  
  // 更复杂的模式匹配
  void handleStatus() {
    switch(this) {
      case OrderStatus.pending:
        sendNotification();
        startProcessingTimer();
      case OrderStatus.processing:
        checkInventory();
      case OrderStatus.shipped when DateTime.now().hour > 17:
        scheduleDeliveryUpdateForTomorrow();
      case OrderStatus.shipped:
        scheduleDeliveryUpdate();
      case OrderStatus.delivered:
        requestReview();
      case OrderStatus.cancelled:
        issueRefund();
        collectFeedback();
    }
  }
}

模式匹配的妙处在于:

  1. 编译器会检查是否覆盖所有情况
  2. 可以使用when条件进行更精细的控制
  3. 代码可读性极高,业务逻辑一目了然

三、枚举与JSON的完美配合

实际开发中经常需要处理JSON序列化。看这个用户权限的例子:

enum UserRole {
  guest(0),
  user(1),
  moderator(2),
  admin(3);

  final int level;
  const UserRole(this.level);
  
  // JSON序列化
  factory UserRole.fromJson(dynamic json) {
    if (json is int) {
      return values.firstWhere(
        (role) => role.level == json,
        orElse: () => guest,
      );
    } else if (json is String) {
      return values.firstWhere(
        (role) => role.name == json.toLowerCase(),
        orElse: () => guest,
      );
    }
    return guest;
  }
  
  dynamic toJson() => level; // 也可以选择输出name
}

// 使用示例
void main() {
  final json = 2;
  final role = UserRole.fromJson(json);
  print(role); // 输出: UserRole.moderator
  print(role.toJson()); // 输出: 2
}

这样处理的好处是:

  • 数据库存储可以用更小的int类型
  • 前后端交互灵活,支持数字和字符串格式
  • 类型安全,避免魔法数字

四、高级技巧:状态机实现

枚举特别适合实现状态机。来看个下载器的例子:

enum DownloadState {
  idle,
  connecting,
  downloading(double progress), // 带参数的状态
  paused,
  completed,
  failed(String error);

  // 状态转换逻辑
  DownloadState transition(dynamic event) {
    switch(this) {
      case idle:
        if (event == 'start') return connecting;
      case connecting:
        if (event == 'connected') return downloading(0.0);
        if (event is String) return failed(event);
      case downloading(progress):
        if (event == 'pause') return paused;
        if (event == 'complete') return completed;
        if (event is double) return downloading(event);
        if (event is String) return failed(event);
      case paused:
        if (event == 'resume') return downloading(0.0);
        if (event == 'cancel') return idle;
      case completed || failed:
        if (event == 'restart') return idle;
    }
    return this; // 保持当前状态
  }
}

// 使用示例
void main() {
  var state = DownloadState.idle;
  state = state.transition('start');
  state = state.transition('connected');
  if (state case downloading(progress: var p)) {
    state = state.transition(0.5); // 更新进度
  }
  print(state); // 输出: downloading(0.5)
}

这种实现方式:

  • 所有状态转换一目了然
  • 非法状态转换会被自动阻止
  • 可以携带状态数据(如下载进度)
  • 编译器会检查是否处理了所有情况

五、实际应用中的注意事项

虽然枚举很强大,但也要注意几点:

  1. 性能考虑:大量使用模式匹配可能影响性能,关键路径要测试

  2. 可维护性:当枚举值超过10个时,考虑是否应该拆分成多个枚举

  3. 序列化陷阱

// 错误示例:忘记处理null情况
UserRole.fromJson(json['role'] ?? 'guest'); // 安全写法
  1. 测试技巧
test('测试所有状态', () {
  for (final status in OrderStatus.values) {
    expect(status.statusMessage, isNotEmpty);
  }
});
  1. 与Freezed配合:考虑使用freezed包生成更强大的枚举类

六、总结

Dart枚举远比你想象的强大。通过本文你应该学会了:

  • 给枚举添加属性和方法
  • 利用模式匹配处理复杂逻辑
  • 实现类型安全的JSON序列化
  • 构建清晰的状态机
  • 避免常见的坑

下次当你需要表示一组固定的状态或类型时,别再简单用字符串或数字了,试试这些枚举的高级用法,让你的代码更安全、更易读、更易维护!