问题
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
回答1:
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());
}
回答2:
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: []
回答3:
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).
回答4:
This example also may help you to understand:
void main() {
var car = new CarProxy(new ProxyObjectImpl('Car'));
testCar(car);
var person = new PersonProxy(new ProxyObjectImpl('Person'));
testPerson(person);
}
void testCar(Car car) {
print(car.motor);
}
void testPerson(Person person) {
print(person.age);
print(person.name);
}
abstract class Car {
String get motor;
}
abstract class Person {
int get age;
String get name;
}
class CarProxy implements Car {
final ProxyObject _proxy;
CarProxy(this._proxy);
noSuchMethod(Invocation invocation) {
return _proxy.handle(invocation);
}
}
class PersonProxy implements Person {
final ProxyObject _proxy;
PersonProxy(this._proxy);
noSuchMethod(Invocation invocation) {
return _proxy.handle(invocation);
}
}
abstract class ProxyObject {
dynamic handle(Invocation invocation);
}
class ProxyObjectImpl implements ProxyObject {
String type;
int id;
Map<Symbol, dynamic> properties;
ProxyObjectImpl(this.type, [this.id]) {
properties = ProxyManager.getProperties(type);
}
dynamic handle(Invocation invocation) {
var memberName = invocation.memberName;
if(invocation.isGetter) {
if(properties.containsKey(memberName)) {
return properties[memberName];
}
}
throw "Runtime Error: $type has no $memberName member";
}
}
class ProxyManager {
static Map<Symbol, dynamic> getProperties(String name) {
Map<Symbol, dynamic> properties = new Map<Symbol, dynamic>();
switch(name) {
case 'Car':
properties[new Symbol('motor')] = 'xPowerDrive2013';
break;
case 'Person':
properties[new Symbol('age')] = 42;
properties[new Symbol('name')] = 'Bobby';
break;
default:
throw new StateError('Entity not found: $name');
}
return properties;
}
}
来源:https://stackoverflow.com/questions/17442581/how-to-use-dart-to-implement-a-delegate-proxy