I have the beginnings of understanding for reflection in Dart.
Coming from a predominantly dynamic language background, I confess that I find most of this painful and awkward. Still, there is something fascinating about it—and not just in a gawking-at-a-car-crash kind of way (though there is definitely some of that). Even if I stick mostly to dynamic languages, this stuff will give me a better appreciation for what is going on. But as Anguar.dart has proven, there is something inherently useful about this by itself.
I left off last night with the ability to reflect on annotations on the current function:
import 'dart:mirrors'; @meta('This is main!') main() { print('[main] Hello!'); var selfMirror = (reflect(main) as ClosureMirror).function; var metaData = selfMirror.metadata; print('[main] Meta is: ${metaData[0].reflectee.note}'); } // The @meta annotation definition class meta { final String note; const meta(this.note); }The above prints out the following when run from the command-line:
[main] Hello! This foo is Bob. [main] Meta is: This is main!What I would like tonight is to figure out how to reflect on class metadata (i.e. annotations). Just to make things interesting, I move my annotation class and the class on which I would like to reflect into a new code file,
foo.dart
:@meta('Foo should not be used in real life') class Foo { String name; Foo(this.name); toString() => "This foo is $name."; } class meta { final String note; const meta(this.note); }Back in
main.dart
, I import foo.dart
, create and use an instance of the imported Foo
to make sure it works, and then reflect on the Foo
class:import 'foo.dart'; import 'dart:mirrors'; @meta('This is main!') main() { print('[main] Hello!'); var selfMirror = (reflect(main) as ClosureMirror).function; var metaData = selfMirror.metadata; print('[main] Meta is: “${metaData[0].reflectee.note}”'); var foo = new Foo('Bob'); print(foo.toString()); var fooMirror = reflect(Foo); print(fooMirror); }This prints out the following when run:
$ dartanalyzer main.dart && dart main.dart Analyzing main.dart... No issues found [main] Hello! [main] Meta is: “This is main!” This foo is Bob. InstanceMirror on Type: class 'Foo'So the
toSting()
method on Foo
seems to work just fine. That is not the problem that I am trying to solve here. What I want is a way to extract metadata from the code. As I found last night, that InstanceMirror
that I get by reflecting on Foo
is not going to help me. I need something that implements a DeclarationMirror
. For last night's function, that was a MethodMirror
. For classes, that would appear to be a ClassMirror
, but I can find no way to get to a ClassMirror
from the reflected InstanceMirror
.The answer turns out to be that I should not be reflecting on the
Foo
class. Instead I should be reflect-classing on the Foo
class with with reflectClass()
function:main() { // ... var fooMirror = reflectClass(Foo); var fooMetaData = fooMirror.metadata; print('[main] Foo meta is: “${fooMetaData[0].reflectee.note}”'); }With that, I am successfully able to grab the note from the
@meta
annotation:$ dartanalyzer main.dart && dart main.dart Analyzing main.dart... No issues found [main] Hello! [main] Meta is: “This is main!” This foo is Bob. [main] Foo meta is: “Foo should not be used in real life”To summarize, to grab metadata from a function, I reflect on the function, access the
function
property from the ClosureMirror
to get a MethodMirror
, which contains the metadata. To grab metadata from a class, I reflect-class on the class, which gives me direct access to the metadata. I do not have high hopes that I am going to remember that next week.Because that is not confusing enough, I find myself wondering about variable declaration annotations. So, what the heck, I annotate the
name
instance variable in my Foo
class with my same custom @meta
annotation:@meta('Foo should not be used in real life') class Foo { @meta('This is the name of the Foo') String name; Foo(this.name); }Getting this meta information at runtime turns out to be fairly easy. Well, maybe not “fairly”—let's call it “relatively” easy. The
ClassMirror
from reflectClass()
has a declarations
Map. I can iterate over that to get any declarations that contain annotation metadata with:main() { // ... var fooMirror = reflectClass(Foo); var fooMetaData = fooMirror.metadata; print('[main] Foo meta is: “${fooMetaData[0].reflectee.note}”'); for (var k in fooMirror.declarations.keys) { if (fooMirror.declarations[k].metadata.length == 0) continue; var _m = fooMirror.declarations[k].metadata; print("[main] $k: ${_m[0].reflectee.note}"); } }I rather like that. It is nice that the
ClassMirror
has such nice information readily available. I'm not saying that I am going to drop all my dynamic languages or anything, but reflecting on variables makes merciful sense to me.Day #58
No comments:
Post a Comment