我希望能够使用 noSuchMethod() 中的查找来支持带有 Map 的动态属性。但是,最新的更改使传入的属性引用名称不可用。我可以理解要求我们使用符号而不是字符串作为名称的缩小方案,但这使得实现可序列化的动态属性变得困难。有人对如何解决这个问题有好的想法吗?
- 我不能使用字符串名称,因为字符串名称在对缩小器的调用之间不是固定的。(这将完全破坏序列化)
我希望能够使用 noSuchMethod() 中的查找来支持带有 Map 的动态属性。但是,最新的更改使传入的属性引用名称不可用。我可以理解要求我们使用符号而不是字符串作为名称的缩小方案,但这使得实现可序列化的动态属性变得困难。有人对如何解决这个问题有好的想法吗?
您可以访问原始名称MirrorSystem.getName(symbol)
所以一个动态类可能看起来像:
import 'dart:mirrors';
class A {
final _properties = new Map<String, Object>();
noSuchMethod(Invocation invocation) {
if (invocation.isAccessor) {
final realName = MirrorSystem.getName(invocation.memberName);
if (invocation.isSetter) {
// for setter realname looks like "prop=" so we remove the "="
final name = realName.substring(0, realName.length - 1);
_properties[name] = invocation.positionalArguments.first;
return;
} else {
return _properties[realName];
}
}
return super.noSuchMethod(invocation);
}
}
main() {
final a = new A();
a.i = 151;
print(a.i); // print 151
a.someMethod(); // throws
}
你可以这样做:
import 'dart:json' as json;
main() {
var t = new Thingy();
print(t.bob());
print(t.jim());
print(json.stringify(t));
}
class Thingy {
Thingy() {
_map[const Symbol('bob')] = "blah";
_map[const Symbol('jim')] = "oi";
}
final Map<Symbol, String> _map = new Map<Symbol, String>();
noSuchMethod(Invocation invocation) {
return _map[invocation.memberName];
}
toJson() => {
'bob': _map[const Symbol('bob')],
'jim': _map[const Symbol('jim')]};
}
更新 - 动态示例:
import 'dart:json' as json;
main() {
var t = new Thingy();
t.add('bob', 'blah');
t.add('jim', 42);
print(t.bob());
print(t.jim());
print(json.stringify(t));
}
class Thingy {
final Map<Symbol, String> _keys = new Map<Symbol, String>();
final Map<Symbol, dynamic> _values = new Map<Symbol, dynamic>();
add(String key, dynamic value) {
_keys[new Symbol(key)] = key;
_values[new Symbol(key)] = value;
}
noSuchMethod(Invocation invocation) {
return _values[invocation.memberName];
}
toJson() {
var map = new Map<String, dynamic>();
_keys.forEach((symbol, name) => map[name] = _values[symbol]);
return map;
}
}
如果只需要“动态属性”,在 Map 中使用 Symbols 作为键就足够了。如果您还想序列化该映射,则需要跟踪原始字符串名称并将其用于序列化。反序列化时,您必须从这些字符串创建新符号。
请注意,所有这些场景(以及基本上涉及的所有内容new Symbol
)都需要编译器创建原始名称到缩小名称的映射,并将此映射放入程序中,这当然会使其更大。
感谢@Alexandre Ardhuin 的解决方案,我进行了一些修改以使其可运行。
import 'dart:mirrors';
class object {
final _properties = new Map<String, Object>();
object();
object.from(Map<String, Object> initial) {
initial.entries.forEach((element) => _properties[element.key] = element.value);
}
noSuchMethod(Invocation invocation) {
if (invocation.isAccessor) {
final realName = MirrorSystem.getName(invocation.memberName);
if (invocation.isSetter) {
// for setter realname looks like "prop=" so we remove the "="
final name = realName.substring(0, realName.length - 1);
_properties[name] = invocation.positionalArguments.first;
return;
} else {
return _properties[realName];
}
}
return super.noSuchMethod(invocation);
}
@override
String toString() {
return _properties.toString();
}
}
main() {
// we can't use var or object type here, because analysis will consider
// https://dart.dev/tools/diagnostic-messages#undefined_setter
// The setter 'i' isn't defined for the type 'object'
// So dynamic is required here!
dynamic a = object.from({'a': 123, 'b': 234});
a.i = 151;
print(a); // print {a: 123, b: 234, i: 151}
try {
a.someMethod(); // throws NoSuchMethodError
} catch (e) {
print(e);
}
}