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