Using reflection

Subscription.packtpub.com/book/web_de…

Dart’s mirror-based reflection API (included in the Dart: Mirrors library) provides a powerful set of tools for reflecting code. This means that you can examine the complete structure of a program and discover all properties of all objects. In this way, methods can be called reflexively. It is even possible to dynamically evaluate code that has not yet been specified in the source code. An example of this is calling a method whose name is supplied as a parameter because it is queried in a database table.

Ready to

The part of your code that uses reflection should have the following import code:

import 'dart:mirrors';
Copy the code

How to do…

To reflect, we do the following:

  • In the Reflection project, we use a class Embrace for reflection.
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:
Copy the code
  • Use MirrorSystem, as shown in the following code:
    final MirrorSystem ms = currentMirrorSystem();
     // Iterating through libraries
    ms
       .libraries
       .forEach((Uri name, LibraryMirror libMirror){
          print('$name $libMirror');
       });
Copy the code

Use 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; }}}}Copy the code
  • Running the previous program produces the following output:
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
Copy the code

How it works…

The currentMirrorSystem class returns the MirrorSystem object for the current quarantine; The Library Getter provides a list of libraries in the scope of the current code.

The InstanceMirror subclass is a representation of an object instance, and the ClassMirror is a representation of the class definition.

Use the top-level Reflect method on an object to get InstanceMirror. This allows you to dynamically call code on the object, generating another InstanceMirror; Using its reflectee property, you can access the actual instance.

Notice that the first argument to the invoke method is the symbol of the method name (identified from its # prefix). Symbols were introduced in Dart because they can survive minimization.

A top-level reflectClass method on a class produces a ClassMirror; You get an object of the same type by calling Type on InstanceMirror of the class. ClassMirror classes have a getter for declarations that returns a mapping from the declared name to their mirror. Static methods can be called on ClassMirror.

For each type of object in Dart, there is a corresponding mirror object. So we have Variabl eMirror, MethodMirror, ClassMirror, LibraryMirror, etc. Calling newInstance on the ClassMirror class with the constructor name as a symbol produces InstanceMirror; Then you can call the methods of the real object through reflectee.

There’s more…

There are a few things we should be aware of when using reflection:

  • The mirroring API is still in development, so expect some additions and tweaks in the future. This implementation is the most complete for code running in the Dart virtual machine.
  • The mirroring in dart2js is a bit behind. The procedures for minifying and tree shaking applications performed by Dart2JS usually do not detect the reflected code. Therefore, using reflection at run time may fail, resulting in noSuchMethod() errors. To prevent this, you can use Mirrors annotations, as shown in the code below, which help the dart2js compiler generate smaller code.
@MirrorsUsed(override:The '*')
import 'dart:mirrors';
Copy the code
  • One limitation is reflection across the quarantine area. At the time of writing, reflection only works if the reflected code and the reflected object are running in the same isolation zone.
  • Suppose you have an undocumented method that returns a Future value, and you want to know the object’s properties and methods without digging into the source code. Run the following code snippet:
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));
}); 
Copy the code

see

When you want to use reflection in code, you must do minimization and tree shaking. Read the recipe for minifying applications in Chapter 1, “Using the Dart Tool.

DartLangSpec – v2.2. PDF

The dart. Dev/guides/lang…

16.8 sign

A symbol literal represents a name, either a valid declaration name or a valid library name in the Dart program.

< symbolLiteral > : : = '#' (hoperatori | (hidentifieri ('. 'hidentifieri) *))Copy the code

A symbol literal #id, where id is an identifier not beginning with an underscore (‘_’), is evaluated as a symbol instance representing identifier ID. All occurrences of # ID evaluate the same instance (Symbol instances are normalized). No other Symbol literal evaluates that Symbol instance or its equivalent (according to the ‘==’ operator 16.27).

A symbol literal #id.id2… Idn, where ID… Idn is an identifier and is evaluated as an instance of a symbol representing the sequence of that identifier. All #id.id2… All occurrences of idN are evaluations of the same instance. No other sign text evaluates the symbol instance or the symbol instance ‘==’ with that instance. This symbolic text represents the name of a library declaration. A library name is not limited by library privacy, even if some of its identifiers start with an underscore.

The Symbol literal #operator is evaluated as an instance of Symbol, representing that particular operator name. All occurrences of #operator evaluate the same instance. No other Symbol literals evaluate the Symbol instance or the Symbol instance associated with the instance ‘==’.

The Symbol literal meaning #_id is evaluated to a Symbol instance representing the private identifier _id that contains the library. In the same library, all occurrences of #_id are evaluations of the same instance. No other Symbol literals evaluate the Symbol instance or the Symbol instance of the instance ‘==’.

Objects created by symbol literals override the ‘==’ operator inherited from the Object class.

One may well ask, what was the motivation for introducing literal symbols? In some languages, symbols are normalized, but strings are not. However strings have been normalized in Dart. Symbols are easier to type than strings, and their use is strangely addictive, but that’s not enough reason to add literal forms to the language. The main motivation has to do with the use of reflection and a network-specific practice called minimization.

Minimize consistently compressing identifiers throughout the program to reduce downloads. This makes it difficult for reflexive programs that are declared through string references. The string references an identifier in the source code, but the identifier is no longer used in the minimized code, and reflection code that uses these will fail. Therefore, Dart reflection uses objects of type Symbol instead of strings. Instances of symbols are guaranteed to be stable in minimization. Providing a literal form for symbols makes reflective code easier to read and write. Symbols are easy to type and often serve as a convenient substitute for enumerations, which is a secondary benefit.

The static type of a symbol literal is a symbol.

Dart API reference documentation — Symbol

API. Dart. Dev/stable/dart…

Instances of symbols created from the same name are equal, but not necessarily identical, but symbols created as compile-time constants are normalized, just as all other constant objects are created.

assert(Symbol("foo") = =Symbol("foo"));
assert(identical(const Symbol("foo"), const Symbol("foo")));
Copy the code

If name is a single identifier that does not begin with an underscore, or a qualified identifier, or an operator name that is different from unary-, the result of the const Symbol(name) is that the Symbol literal created by prefixing the name content with # will be evaluated as the same instance.

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));
Copy the code

This constructor cannot create a Symbol instance equivalent to a private Symbol literal, such as #_foo.

const Symbol("_foo") // Invalid
Copy the code

The created instance overrides Object.==.

The following text is non-normative.

Creating a nonconst Symbol instance may result in a larger output. If possible, use MirrorsUsed in “DART :mirrors” to specify which names may be passed to this constructor.

reference

  • Stackoverflow.com/questions/5…
  • Dev. To/graphicbeac…
  • Medium.com/swlh/dart-a…