能否编写支持多种枚举类型的通用序列化反序列化扩展?
解决Dart枚举序列化/反序列化重复扩展的问题
你的痛点我太懂了——每个枚举都写一遍几乎一模一样的扩展,不仅重复劳动,还容易出错。好在Dart有几种方式可以帮你摆脱这种重复代码:
方法1:写一个泛型通用扩展(原生支持,无需第三方包)
Dart的枚举都继承自Enum类,我们可以基于这个写一个泛型扩展,让所有枚举都能复用序列化/反序列化逻辑:
// 自定义错误类(和你原来的保持一致) class SerializerError extends Error { final String message; SerializerError(this.message); @override String toString() => message; } // 通用枚举序列化扩展 extension EnumSerialization<T extends Enum> on T { // 序列化:直接用枚举自带的name属性(和你原来的split逻辑效果完全一致,但更高效) String getString() => name; // 反序列化:需要传入目标枚举的values列表,指定泛型类型 static T fromString(List<T> enumValues, String str) => enumValues.firstWhere( (v) => v.name == str, orElse: () => throw SerializerError('Invalid string "$str" for enum ${T.runtimeType}'), ); }
使用方式:
对于任意枚举,比如MyEnum和OtherEnum:
enum MyEnum { apple, banana } enum OtherEnum { small, large } // 序列化 MyEnum.apple.getString(); // 返回 "apple" OtherEnum.large.getString(); // 返回 "large" // 反序列化 final myEnumValue = EnumSerialization.fromString(MyEnum.values, "apple"); // 得到 MyEnum.apple final otherEnumValue = EnumSerialization.fromString(OtherEnum.values, "large"); // 得到 OtherEnum.large
如果觉得反序列化时写EnumSerialization.fromString有点长,也可以给每个枚举加一个极简的扩展,只做转发:
extension MyEnumHelpers on MyEnum { static MyEnum fromString(String str) => EnumSerialization.fromString(MyEnum.values, str); } // 现在可以直接这样用: MyEnum.fromString("banana");
这样每个枚举的扩展只需要一行代码,彻底避免重复逻辑。
方法2:用代码生成自动生成(适合大量枚举的场景)
如果你的项目里枚举非常多,甚至想自动生成JSON序列化逻辑,可以用json_serializable包(Dart生态常用的代码生成工具):
- 先在
pubspec.yaml里添加依赖:
dependencies: json_annotation: ^4.8.1 dev_dependencies: build_runner: ^2.4.4 json_serializable: ^6.7.1
- 给枚举加注解:
import 'package:json_annotation/json_annotation.dart'; part 'my_enum.g.dart'; @JsonEnum(fieldRename: FieldRename.snake_case) // 可选,比如把驼峰转蛇形 enum MyEnum { apple, banana } // 执行代码生成命令:flutter pub run build_runner build
- 生成后会自动得到
fromJson和toJson方法:
// 序列化 MyEnum.apple.toJson(); // 返回 "apple" // 反序列化 MyEnum.fromJson("banana"); // 得到 MyEnum.banana
这种方式完全不用手动写任何序列化逻辑,代码生成器会帮你搞定一切,还能支持自定义命名规则(比如驼峰转蛇形、大写转小写等)。
补充说明:为什么不用你原来的toString().split('.').last?
Dart的枚举自带name属性,它的值就是枚举值的标识符(比如MyEnum.apple.name就是"apple"),和你原来的toString().split('.').last效果完全一样,但name是直接返回字符串,比拆分toString的结果更高效,也更可靠。
内容的提问来源于stack exchange,提问作者ivanesi
相关产品推荐
相关产品推荐

