[Dart筆記]Reflection & Symbol

使用反射

subscription.packtpub.com/book/web_de…html

Dart基於鏡像的反射API(包含在dart:mirrors庫中)提供了一套強大的工具來反射代碼。這意味着能夠檢討一個程序的完整結構,發現全部對象的全部屬性。經過這種方式,方法能夠被反射性地調用。甚至有可能動態地評估源代碼中還沒有指定的代碼。這方面的一個例子是調用一個方法,該方法的名稱被做爲參數提供,由於它在數據庫表中被查詢到。web

準備好

你的代碼中使用反射的部分應該有如下導入代碼:數據庫

import 'dart:mirrors';
複製代碼

如何作...

爲了進行反射,咱們進行如下操做:api

  • 在反射項目中,咱們使用一個類Embrace來進行反射。
void main() {
    var embr = new Embrace(5);
    print(embr);  // Embraceometer reads 5
    embr.strength += 5;
    print(embr.toJson()); // {strength: 10}
    var embr2 = new Embrace(10);
    var bigHug = embr + embr2;
    // Start reflection code:
複製代碼
  • 使用MirrorSystem,如如下代碼所示:
final MirrorSystem ms = currentMirrorSystem();
     // Iterating through libraries
    ms
       .libraries
       .forEach((Uri name, LibraryMirror libMirror){
          print('$name $libMirror');
       });
複製代碼

使用InstanceMirror和ClassMirror,如如下代碼所示:markdown

InstanceMirror im = reflect(embr);
    InstanceMirror im2 = im.invoke(#toJson, []);
    print(im2.reflectee); // {strength: 10}
    ClassMirror cm = reflectClass(Embrace);
    ClassMirror cm2 = im.type;
    printAllDeclarationsOf(cm);
    InstanceMirror im3 = cm.newInstance(#light, []);
    print(im3.reflectee.strength);
    im3.reflectee.withAffection();
}

printAllDeclarationsOf(ClassMirror cm) {
  for (var k in cm.declarations.keys)
    print(MirrorSystem.getName(k));
  print(MirrorSystem.getName(m.simpleName));
}

class Embrace {
   num _strength;
   num get strength => _strength;
   set strength(num value) => _strength=value;
   Embrace(this._strength);
   Embrace.light(): _strength=3;
   Embrace.strangle(): _strength=100;
   Embrace operator +(Embrace other) => new Embrace(strength + other.strength);
   String toString() => "Embraceometer reads $strength";
   Map toJson() => {'strength': '$_strength'};
   
   withAffection() {
     for (var no=0; no <= 3; no++) {
       for (var s=0; s <=5; s++) { strength = s; }
     }
   }
}
複製代碼
  • 運行前面的程序產生如下輸出:
Embraceometer reads 5
{strength: 10}
dart:core LibraryMirror on 'dart.core'
dart:mirrors LibraryMirror on
'dart.mirrors'
dart:nativewrappers LibraryMirror on ''
dart:typed_data LibraryMirror on 'dart.typed_data'
dart:async LibraryMirror on 'dart.async'
dart:convert LibraryMirror on 'dart.convert'
dart:collection LibraryMirror on 'dart.collection'
dart:_internal LibraryMirror on 'dart._internal@0x1f109d24'
dart:isolate LibraryMirror on 'dart.isolate' dart:math LibraryMirror on 'dart.math' dart:builtin LibraryMirror on 'builtin' dart:io LibraryMirror on 'dart.io' file:///F:/Dartiverse/ADartCookbook/book/Chapter 4 - Object orientation/code/reflection/bin/reflection.dart LibraryMirror on ''
{strength: 10}
_strength
strength
strength=
+
toString
toJson
withAffection
Embrace
Embrace.light
Embrace.strangle
3
複製代碼

它是如何工做的...

currentMirrorSystem類返回當前隔離區的MirrorSystem對象;library getter提供當前代碼範圍內的庫列表。網絡

InstanceMirror子類是一個對象實例的表示,ClassMirror是類定義的表示。app

在一個對象上使用頂級的reflect方法來得到InstanceMirror。這容許你在對象上動態地調用代碼,產生另外一個InstanceMirror;使用它的reflectee屬性,你能夠訪問實際的實例。async

請注意,invoke方法的第一個參數是方法名稱的符號(可從其#前綴中識別)。符號是在Dart中引入的,由於它們能夠在最小化過程當中生存。ide

在一個類上的頂級reflectClass方法會產生ClassMirror;經過在該類的InstanceMirror上調用type,能夠獲得相同類型的對象。ClassMirror類有一個聲明的getter,返回一個從聲明的名字到它們的鏡像的映射。靜態方法能夠在ClassMirror上調用。函數

對於Dart中的每一種類型的對象,都存在一個相應的鏡像對象。因此咱們有Variabl eMirror, MethodMirror, ClassMirror, LibraryMirror,等等。在ClassMirror類上調用newInstance,並將構造函數的名稱做爲一個符號,會產生InstanceMirror;而後你能夠經過reflectee調用真實對象的方法。

還有更多...

在使用反射時,有一些事情咱們應該注意:

  • 鏡像API仍在發展中,因此預計將來會有一些補充和調整。對於在Dart虛擬機中運行的代碼來講,該實現是最完整的。
  • dart2js中的鏡像有點滯後。由dart2js執行的minifying和tree shaking應用程序的過程一般不會檢測到反射的代碼。所以在運行時使用反射可能會失敗,致使noSuchMethod()錯誤。爲了防止這種狀況發生,可使用Mirrors註解,以下面的代碼所示,這有助於dart2js編譯器生成更小的代碼。
@MirrorsUsed(override:'*')
import 'dart:mirrors';
複製代碼
  • 其中一個限制是跨隔離區的反射。在寫這本書的時候,只有當反射代碼和被反射的對象在同一個隔離區內運行時,反射才能發揮做用。
  • 假設你有一個返回Future值的無文檔的方法,你想知道該對象的屬性和方法,而不用挖掘源代碼。運行下面的代碼片段:
import 'dart:mirrors';

undocumentedMethod().then((unknown){
      var r = reflect(unknown).type; // ClassMirror
      var m = r.declarations;
for (var k in m.declarations.key) print(MirrorSystem.getName(k));
}); 
複製代碼

參見

當你想在代碼中使用反射時,必須進行最小化和樹形搖動,請閱讀第1章 "使用Dart工具 "中的縮小應用程序的配方

DartLangSpec-v2.2.pdf

dart.dev/guides/lang…

16.8 符號

符號字面表示一個名稱,在Dart程序中是一個有效的聲明名稱或一個有效的庫名稱。

<symbolLiteral> ::= ‘#’ (hoperatori | (hidentifieri (‘.’ hidentifieri)*))
複製代碼

一個符號字面#id,其中id是一個不如下劃線('_')開頭的標識符,評估爲一個表明標識符id的符號實例。全部#id的出現都是對同一個實例進行評估(符號實例是規範化的),沒有其餘的符號字面會對該Symbol實例或與該實例相等的Symbol實例進行評估(根據'=='操做符16.27)。

一個符號字面#id.id2...idn,其中id...idn是標識符,評估爲表明該標識符序列的符號實例。全部具備相同標識符序列的#id.id2...idn的出現都是對同一個實例進行求值,沒有其餘的符號文字對該符號實例或對與該實例'=='的符號實例進行求值。這種符號文字表示一個庫聲明的名稱。庫名稱不受庫隱私的限制,即便它的一些標識符如下劃線開始。

符號字面#operator評估爲Symbol的一個實例,表明那個特定的operator名稱。全部#operator的出現都是對同一個實例進行評估,其餘的符號字面都不會對該Symbol實例或對與該實例'=='的Symbol實例進行評估。

符號字面意義#_id,評估到一個Symbol實例,表明包含庫的私有標識符_id。在同一個庫中,#_id的全部出現都是對同一個實例的評估,沒有其餘的符號字面會對該Symbol實例或對該實例'=='的Symbol實例的評估。

符號字面所建立的對象都覆蓋了從Object類繼承的'=='操做符。

人們極可能會問,引入字面符號的動機是什麼?在一些語言中,符號是規範化的,而字符串則不是。然而字符串在Dart中已經被規範化了。符號比字符串更容易輸入,並且它們的使用會讓人奇怪地上癮,但這還不足以成爲在語言中添加字面形式的理由。主要的動機是與反射的使用和一種被稱爲最小化的網絡具體作法有關。

最小化在整個程序中持續壓縮標識符,以減小下載量。這種作法給那些經過字符串引用程序聲明的反射性程序帶來了困難。字符串會在源碼中引用一個標識符,但該標識符在最小化後的代碼中再也不使用,使用這些的反射代碼會失敗。所以,Dart反射使用Symbol類型的對象而不是字符串。符號的實例被保證在最小化方面是穩定的。爲符號提供字面形式使反射性代碼更容易閱讀和編寫。符號很容易輸入,而且一般能夠做爲枚舉的方便替代物,這是次要的好處。

符號字面的靜態類型是符號。

Dart API reference documentation -- Symbol

api.dart.dev/stable/dart…

從相同名稱建立的符號實例是平等的,但不必定相同,但做爲編譯時常量建立的符號是規範化的,如同全部其餘常量對象的建立。

assert(Symbol("foo") == Symbol("foo"));
assert(identical(const Symbol("foo"), const Symbol("foo")));
複製代碼

若是name是一個不如下劃線開頭的單一標識符,或者是一個限定的標識符,或者是一個不一樣於unary-的操做符名稱,那麼const Symbol(name)的結果就是經過在name的內容前綴#而建立的符號字面將評估爲同一個實例。

assert(Symbol("foo") == #foo);
assert(Symbol("[]=") == #[]=]);
assert(Symbol("foo.bar") == #foo.bar);
assert(identical(const Symbol("foo"), #foo));
assert(identical(const Symbol("[]="), #[]=));
assert(identical(const Symbol("foo.bar"), #foo.bar));
複製代碼

這個構造函數不能建立一個等同於私有符號字面的Symbol實例,好比#_foo。

const Symbol("_foo") // Invalid
複製代碼

建立的實例會覆蓋Object.==。

下面的文字是非規範性的。

建立非const的Symbol實例可能會致使更大的輸出。若是可能的話,使用 "dart:mirrors "中的MirrorsUsed來指定哪些名字可能被傳遞給這個構造函數。

參考

相關文章
相關標籤/搜索