如何在Dart中实现动态属性?

时间:2013-05-02 03:32:08

标签: dart

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

  • 我不能使用String名称,因为在对minifier的调用之间没有修复String名称。 (这将彻底打破序列化)

4 个答案:

答案 0 :(得分:8)

您可以使用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
}

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

你可以这样做:

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;
    }
}

答案 3 :(得分: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);
  }
}