I have two classes Parser
and Proxy
, and when I invoke a method from Parser
, which does not exist, it will delegate it to Proxy
class.
My code:
class Parser {
noSuchMethod(Invocation invocation) {
// how to pass the `invocation` to `Proxy`???
}
}
class Proxy {
static String hello() { return "hello"; }
static String world() { return "world"; }
}
That when I write:
var parser = new Parser();
print(parser.hello());
It will print:
hello
You have to use dart:mirrors. Here's how to do :
import 'dart:mirrors';
class Parser {
noSuchMethod(Invocation invocation) {
ClassMirror cm = reflectClass(Proxy);
return cm.invoke(invocation.memberName
, invocation.positionalArguments
/*, invocation.namedArguments*/ // not implemented yet
).reflectee;
}
}
class Proxy {
static String hello() { return "hello"; }
static String world() { return "world"; }
}
main(){
var parser = new Parser();
print(parser.hello());
print(parser.world());
}
Alexandre's answer is correct, but I'd like to add something.
I assume that the delegation to Proxy
is an implementation detail, and we don't want the user to be exposed to it. In that case, we should have some handling of cases where methods are called on parser
that are not supported by Proxy
. Right now, if you do this:
void main() {
var parser = new Parser();
print(parser.foo());
}
You get this error:
Unhandled exception:
Compile-time error during mirrored execution: <Dart_Invoke: did not find static method 'Proxy.foo'.>
I would write the code in noSuchMethod
a little differently. Before delegating to Proxy
, I would check that Proxy
supports the method I'm about to invoke. If Proxy
supports it, I would invoke the method on Proxy
as Alexandre describes in his answer. If Proxy
does not support the method, I would throw a NoSuchMethodError
.
Here is a revised version of the answer:
import 'dart:mirrors';
class Parser {
noSuchMethod(Invocation invocation) {
ClassMirror cm = reflectClass(Proxy);
if (cm.methods.keys.contains(invocation.memberName)) {
return cm.invoke(invocation.memberName
, invocation.positionalArguments
/*, invocation.namedArguments*/ // not implemented yet
).reflectee;
}
throw new NoSuchMethodError(this,
_symbolToString(invocation.memberName),
invocation.positionalArguments,
_symbolMapToStringMap(invocation.namedArguments));
}
}
String _symbolToString(Symbol symbol) => MirrorSystem.getName(symbol);
Map<String, dynamic> _symbolMapToStringMap(Map<Symbol, dynamic> map) {
if (map == null) return null;
var result = new Map<String, dynamic>();
map.forEach((Symbol key, value) {
result[_symbolToString(key)] = value;
});
return result;
}
class Proxy {
static String hello() { return "hello"; }
static String world() { return "world"; }
}
main(){
var parser = new Parser();
print(parser.hello());
print(parser.world());
print(parser.foo());
}
And here is the output from running this code:
hello
world
Unhandled exception:
NoSuchMethodError : method not found: 'foo'
Receiver: Instance of 'Parser'
Arguments: []
I'll also add that you could avoid the use of mirrors if the set of things you want to delegate to is fixed and you can reasonably hard-code it. That's particularly easy if you're using static methods, but I'm not clear why you're doing that here. I think the following would work for both instance methods and static methods, but I'm typing this code in without actually trying it...
Function lookupMethod(Proxy p, Symbol name) {
if (name == const Symbol("hello")) return p.hello;
if (name == const Symbol("world")) return p.world;
throw "Aaaaaagh";
}
noSuchMethod(invocation) =>
Function.apply(lookupMethod(Proxy, invocation.memberName),
invocation.positionalArguments);
This is fragile if the set of forwarded methods changes, but may help avoid the code size increase if you use mirrors (which at the moment disables almost all tree-shaking).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With