The Dart mirror-based reflection API (contained in the dart:mirrors
library) provides a powerful set of tools to reflect on code. This means that it is possible to introspect the complete structure of a program and discover all the properties of all the objects. In this way, methods can be invoked reflectively. It will even become possible to dynamically evaluate code that was not yet specified literally in the source code. An example of this would be calling a method whose name was provided as an argument because it is looked up in the database table.
The part of your code that uses reflection should have the following import code:
import 'dart:mirrors';
To perform reflection we perform the following actions:
reflection
project, we use a class Embrace
to reflect upon: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
, as shown in the following code:final MirrorSystem ms = currentMirrorSystem(); // Iterating through libraries ms .libraries .forEach((Uri name, LibraryMirror libMirror){ print('$name $libMirror'), });
InstanceMirror
and ClassMirror
, as shown in the following code: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
The currentMirrorSystem
class returns a MirrorSystem
object on the current isolate; the libraries
getter gives you the list of libraries in the scope of the current code.
The InstanceMirror
subclass is a representation of an instance of an object and ClassMirror
is the representation of the class definition.
Use the top-level reflect
method on an object to get InstanceMirror
. This allows you to dynamically invoke code on the object producing another InstanceMirror
; using its reflectee
property gives you access to the actual instance.
Note that the invoke method takes as its first argument a symbol (recognizable from its #
prefix) for the method name. Symbols were introduced in Dart because they survive minification.
The top-level reflectClass
method on a class results in ClassMirror
; the same type of object is given by calling type
on InstanceMirror
of that class. The ClassMirror
class has a declarations
getter that returns a map from the names of the declarations to the mirrors on them. Static methods can be called on ClassMirror
.
For every type of object in Dart, there exists a corresponding mirror object. So we have Variabl
eMirror
, MethodMirror
, ClassMirror
, LibraryMirror
, and so on. Invoking newInstance
on a ClassMirror
class with the name of a constructor as a symbol produces InstanceMirror
; you can then call methods on the real object via reflectee
.
There are some things we should be aware of when using reflection:
noSuchMethod()
errors. To prevent this from happening, use the Mirrors
annotation, as shown in the following code, which helps the dart2js compiler to generate a smaller code:@MirrorsUsed(override:'*') import 'dart:mirrors';
import 'dart:mirrors'; undocumentedMethod().then((unknown){ var r = reflect(unknown).type; // ClassMirror var m = r.declarations; for (var k in m.declarations.keys) print(MirrorSystem.getName(k)); });