我使用下面的内容来了解如何将dart调用方法传入其他方法,以查看传入方法将在/可以调用的上下文。
void main() {
var one = new IDable(1);
var two = new IDable(2);
print('one ${caller(one.getMyId)}'); //one 1
print('two ${caller(two.getMyId)}'); //two 2
print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception
}
class IDable{
int id;
IDable(this.id);
int getMyId(){
return id;
}
}
caller(fn){
return fn();
}
callerJustForThree(fn){
var three = new IDable(3);
three.fn();
}
那么caller
经理如何在没有上下文fn
的情况下调用其参数one.fn()
,以及为什么callerJustForThree
无法调用传入fn
在为其定义了该函数的对象上?
答案 0 :(得分:4)
在Dart中,声明为类的一部分的实例方法与其他函数(如闭包和静态函数)之间存在差异。
实例方法是唯一可以访问this
的方法(构造函数除外)。从概念上讲,它们是类描述的一部分,而不是对象。也就是说,当您进行方法调用时o.foo()
Dart首先提取o
的类类型。然后它在类描述中搜索foo
(如果需要,递归地遍历超类)。最后,它将this
设置为o
。
除了能够调用对象上的方法(o.foo()
)之外,还可以获得绑定闭包:o.foo
(没有调用的括号)。然而,这是至关重要的,这种形式只是(<args>) => o.foo(<args>)
的语法糖。也就是说,这只会创建一个新的闭包,捕获o
并将对它的调用重定向到实例方法。
这整个设置有几个重要的后果:
您可以撕掉实例方法并获取绑定闭包。 o.foo
的结果会自动绑定到o
。不需要自己绑定它(但也无法将其绑定到不同的实例)。这是一种方式,在您的示例中,one.getMyId
有效。您实际上得到了以下关闭:() => one.getMyId()
而不是。{/ p>
无法向对象添加或删除方法。您需要更改类描述,这是(有意)不支持的。
var f = o.foo;
意味着你会一直关闭。这意味着您不能将此绑定闭包用作哈希表中的键。例如,register(o.foo)
后跟unregister(o.foo)
很可能无效,因为每个o.foo都会有所不同。您可以尝试print(o.foo == o.foo)
。
您无法将方法从一个对象传输到另一个对象。但是,您尝试访问实例方法时,它们将始终受到约束。
看看你的例子:
print('one ${caller(one.getMyId)}'); //one 1
print('two ${caller(two.getMyId)}'); //two 2
print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception
这些行相当于:
print('one ${caller(() => one.getMyId())}');
print('two ${caller(() => two.getMyId())}');
print('one ${callerJustForThree(() => one.getMyId())}';
内部callerJustForThree
:
callerJustForThree(fn){
var three = new IDable(3);
three.fn();
}
完全忽略给定的参数fn
。在最后一行中执行three.fn()
时,Dart会找到three
的类描述(IDable
),然后在其中搜索fn
。由于它没有找到,它会调用noSuchMethod
后备。 fn
参数被忽略。
如果要根据某个参数调用实例成员,可以按如下方式重写最后一个示例:
main() {
...
callerJustForThree((o) => o.getMyId());
}
callerJustForThree(invokeIDableMember){
var three = new IDable(3);
invokeIDableMember(three);
}
答案 1 :(得分:0)
我会试着解释,这不一定是我的力量。如果我写的东西不可理解,请随时给我一个大喊。
将方法视为普通对象,也像其他变量一样。
当您调用caller(one.getMyId)
时,您实际上并没有传递对类定义方法的引用 - 您传递了特定于one
的方法“object”。
在callerJustForThree
中,您传递实例one
的相同方法“对象”。但你不要叫它。如果您的方法调用范围中的对象fn
,则调用实例fn
的对象three
,而不是存在,因为您没有在上课。
使用常规变量考虑此代码:
void main() {
var one = new IDable(1);
var two = new IDable(2);
caller(one.id);
caller(two.id);
callerJustForThree(one.id);
}
class IDable{
int id;
IDable(this.id);
}
caller(param){
print(param);
}
callerJustForThree(param){
var three = new IDable(3);
print(three.id); // This works
print(param); // This works, too
print(three.param); // But why should this work?
}
这是完全相同的概念。将您的回调视为正常变量,一切都有意义。至少我希望如此,如果我解释得足够好。