无需dart:mirrors,如何在Dart中指向字段?动态Model问题求解
嘿,刚好之前也踩过类似的坑,给你分享几个靠谱的方案,完全避开dart:mirrors,还能适配dynamic类型的Model对象:
一、不使用dart:mirrors的字段指向方法
1. 手动维护字段访问映射表
这是最直接且类型安全的方式,给你的Model类加静态的getter/setter映射,把字段名和对应的访问逻辑绑定:
class Model { int counter = 0; // 可以扩展其他字段 // 字段读取映射 static final Map<String, dynamic Function(Model)> fieldGetters = { 'counter': (model) => model.counter, // 其他字段的getter同理添加 }; // 字段写入映射 static final Map<String, void Function(Model, dynamic)> fieldSetters = { 'counter': (model, value) => model.counter = value as int, // 其他字段的setter同理添加 }; }
使用的时候就像这样:
final model = Model(); // 读取counter final currentCounter = Model.fieldGetters['counter']!(model); // 修改counter Model.fieldSetters['counter']!(model, currentCounter + 1);
这个方法的好处是完全可控,没有额外依赖,而且因为直接引用了counter字段,tree shaking绝对不会把它移除——毕竟代码里明确调用了它。
2. 用代码生成工具自动生成访问器
如果你的Model类字段很多,手动写映射表太麻烦,可以用build_runner配合reflectable(dart官方推荐的mirrors替代方案,支持tree shaking)来自动生成代码:
首先给Model类加注解:
import 'package:reflectable/reflectable.dart'; // 定义反射能力,这里只需要getter和setter class FieldReflector extends Reflectable { const FieldReflector() : super(invokingCapability); } const fieldReflector = FieldReflector(); // 给Model类添加注解,让reflectable生成反射代码 @fieldReflector class Model { int counter = 0; }
运行dart run build_runner build生成反射代码后,就能动态访问字段了:
final model = Model(); // 获取实例的反射镜像 final mirror = fieldReflector.reflect(model); // 读取counter final counter = mirror.invokeGetter('counter'); // 修改counter mirror.invokeSetter('counter', 6);
生成的代码里明确引用了counter字段,所以也不用担心tree shaking的问题。
二、dynamic类型Model对象的字段指向
如果你的对象是dynamic类型,核心思路是先确保类型安全,再用上面的方法:
1. 类型转换后使用映射表
如果你能确定dynamic对象就是Model实例,直接做类型转换即可:
dynamic dynamicModel = Model(); if (dynamicModel is Model) { // 接下来就和普通Model实例一样用映射表访问字段 final counter = Model.fieldGetters['counter']!(dynamicModel); }
2. 用noSuchMethod兜底(适合不确定类型的场景)
如果无法提前确定类型,或者需要更灵活的动态访问,可以在Model类里重写noSuchMethod来捕获字段的get/set调用:
class Model { int counter = 0; @override dynamic noSuchMethod(Invocation invocation) { // 处理getter调用 if (invocation.isGetter) { final fieldName = MirrorSystem.getName(invocation.memberName); if (fieldName == 'counter') { return counter; } } // 处理setter调用 else if (invocation.isSetter) { final fieldName = MirrorSystem.getName(invocation.memberName).replaceAll('=', ''); if (fieldName == 'counter') { counter = invocation.positionalArguments.first as int; return null; } } // 其他情况交给父类处理 return super.noSuchMethod(invocation); } }
这样即使是dynamic类型的对象,调用dynamicModel.counter或者dynamicModel.counter = 10时,都会触发noSuchMethod里的逻辑,实现字段访问。
总结
- 小项目/字段少:优先用手动映射表,简单直接无依赖;
- 大项目/字段多:用reflectable代码生成,减少重复劳动;
- dynamic对象:优先做类型转换,不确定类型再用
noSuchMethod兜底。
所有方案都不会用到dart:mirrors,而且因为都明确引用了counter字段,完全不用担心tree shaking会把它移除。
内容的提问来源于stack exchange,提问作者Tree

