2013-05-02 39 views
5

我希望能夠使用noSuchMethod()中的查找來支持Map的動態屬性。但是,最新更改會使傳入屬性引用名稱不可用。我可以理解縮小方案要求我們使用符號而不是字符串作爲名稱,但是這使得實現可序列化的動態屬性變得困難。任何人都有如何解決這個問題的好主意?如何在Dart中實現動態屬性?

  • 我無法使用字符串名稱,因爲字符串名稱在調用縮小器之間不固定。 (這將完全破壞序列化)

回答

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 
} 
+0

是真正的名字格式,它相當穩定,或者每當新版本的dart出現時,我不得不擔心更新我的代碼? – jz87 2013-05-02 07:47:44

+0

我真的不知道格式是否被指定,但在[語言規範](https://www.dartlang.org/docs/spec/latest/dart-language-specification.html)中有引用_setter' v ='_。所以我會說它幾乎被定義。隨意問在郵件列表或其他SO問題:) – 2013-05-02 08:39:09

0

如果您只需要「動態屬性」,它應該足以在地圖中使用符號作爲鍵。如果您還想序列化該映射,則需要跟蹤原始字符串名稱並將其用於序列化。反序列化時,你必須從這些字符串中創建新的符號。

請注意,所有這些場景(基本上涉及new Symbol的所有內容)都需要一個編譯器來創建原始名稱與縮小比例的映射,並將該映射放入程序中,這當然會使其變得更大。

+0

是的問題是我如何獲得原始字符串名稱的訪問? noSuchMethod不會公開原始字符串,所有從Invocation對象獲得的都是符號。 – jz87 2013-05-02 04:29:19

+0

那麼,當你將東西放入地圖時,你必須記錄原來的名字。編輯:噢,接受的答案是正確的,有一個向後的映射。不記得那件事。 – Ladicek 2013-05-02 10:04:22

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

我真正想要的就像是一個Expando對象。我希望能夠使用固定屬性和可以在運行時添加的其他屬性來定義Thingy。 – jz87 2013-05-02 06:06:28

+0

我已經添加了一個動態示例 - 是您的意思?這樣做的好處是您不必使用反射,這意味着它可以更好地與dart2js配合使用。它也應該很容易處理setter,而不是使用add - 你可以在上面的Alexadres的例子中使用一些代碼。 – 2013-05-02 21:38:25

+0

糟糕 - 你顯然無法處理沒有mirror.GetName()的setter。 – 2013-05-02 22:01:51