2

我使用下面的内容来查看 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();
}

那么callermanager如何在fn没有上下文的情况下调用它的参数 ie one.fn(),以及为什么无法在为其定义了该函数的对象上callerJustForThree调用传入的参数?fn

4

2 回答 2

4

在 Dart 中,声明为类的一部分的实例方法与其他函数(如闭包和静态函数)之间存在差异。

实例方法是唯一可以访问this. 从概念上讲,它们是类描述的一部分,而不是对象。也就是说,当你进行方法调用时,o.foo()Dart 首先提取o. 然后它foo在类描述中搜索(如果需要,递归遍历超类)。最后,它将找到的方法应用于thisset to o

除了能够调用对象(o.foo())上的方法之外,还可以获得绑定闭包:(o.foo没有用于调用的括号)。然而,这是至关重要的,这种形式只是(<args>) => o.foo(<args>). 也就是说,这只是创建了一个新的闭包,该闭包捕获o并将对它的调用重定向到实例方法。

整个设置有几个重要的后果:

  • 您可以撕下实例方法并获得绑定闭包。的结果o.foo自动绑定到o. 无需自己绑定(但也无法将其绑定到不同的实例)。在您的示例中,这是有效的方式one.getMyId。您实际上得到了以下关闭:() => one.getMyId()相反。

  • 无法向对象添加或删除方法。您需要更改类描述,这是(故意)不支持的。

  • 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被完全忽略。在最后一行做的时候,Dart 会找到(which is )three.fn()的类描述,然后在里面搜索。由于它没有找到一个,它会调用回退。参数被忽略。threeIDablefnnoSuchMethodfn

如果要根据某个参数调用实例成员,可以将最后一个示例重写如下:

main() {
  ...
  callerJustForThree((o) => o.getMyId());
}

callerJustForThree(invokeIDableMember){
  var three = new IDable(3);
  invokeIDableMember(three);
}
于 2013-05-09T10:22:26.353 回答
0

我会尽力解释,这不一定是我的强项。如果我写的东西不能理解,请随时给我留言。

将方法视为普通对象,就像其他所有变量一样。

当您调用时caller(one.getMyId),您并没有真正传递对类定义方法的引用 - 您传递的是特定于 instance 的方法“对象” one

callerJustForThree中,您传递了 instance 的相同方法“对象” 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?
}

这是完全相同的概念。将您的回调视为普通变量,一切都有意义。至少我希望如此,如果我解释得足够好的话。

于 2013-05-08T22:31:08.203 回答