10

我希望能够使用 noSuchMethod() 中的查找来支持带有 Map 的动态属性。但是,最新的更改使传入的属性引用名称不可用。我可以理解要求我们使用符号而不是字符串作为名称的缩小方案,但这使得实现可序列化的动态属性变得困难。有人对如何解决这个问题有好的想法吗?

  • 我不能使用字符串名称,因为字符串名称在对缩小器的调用之间不是固定的。(这将完全破坏序列化)
4

4 回答 4

12

您可以访问原始名称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
}
于 2013-05-02T07:37:05.483 回答
1

你可以这样做:

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;
    }
}
于 2013-05-02T05:36:58.880 回答
0

如果只需要“动态属性”,在 Map 中使用 Symbols 作为键就足够了。如果您还想序列化该映射,则需要跟踪原始字符串名称并将其用于序列化。反序列化时,您必须从这些字符串创建新符号。

请注意,所有这些场景(以及基本上涉及的所有内容new Symbol)都需要编译器创建原始名称到缩小名称的映射,并将此映射放入程序中,这当然会使其更大。

于 2013-05-02T04:22:14.927 回答
0

感谢@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);
  }
}

于 2020-08-04T02:29:42.277 回答