2

以下を使用して、ダーツが他のメソッドに渡されたメソッドを呼び出す方法を確認し、渡されたメソッドがどのコンテキストで呼び出されるか、または呼び出されるかを確認しました。

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引数を呼び出すのでしょうか。fnone.fn()callerJustForThreefn

4

2 に答える 2

4

Dart では、クラスの一部として宣言されたインスタンス メソッドと、他の関数 (クロージャーや静的関数など) との間に違いがあります。

にアクセスできるのは、インスタンス メソッドだけです (コンストラクターを除く) this。概念的には、オブジェクトではなくクラス記述の一部です。つまり、メソッド呼び出しを行うと、o.foo()Dart は最初に のクラス タイプを抽出しoます。次に、クラスの説明で を検索しますfoo(必要に応じてスーパー クラスを再帰的に調べます)。最後に、見つかったメソッドをthisset に適用しoます。

オブジェクト ( o.foo()) でメソッドを呼び出すことができることに加えて、バインドされたクロージャーを取得することもできます: o.foo(呼び出しの括弧なし)。ただし、これは非常に重要です。この形式は、 の構文糖衣にすぎません(<args>) => o.foo(<args>)。つまり、これはo呼び出しをキャプチャしてインスタンス メソッドにリダイレクトする新しいクロージャを作成するだけです。

このセットアップ全体には、いくつかの重要な結果があります。

  • インスタンス メソッドを切り離して、バインドされたクロージャを取得できます。の結果o.fooは自動的に にバインドされoます。自分でバインドする必要はありません (ただし、別のインスタンスにバインドする方法もありません)。これは、あなたの例ではone.getMyIdうまくいく方法です。実際には次のクロージャを取得しています:() => one.getMyId()代わりに。

  • メソッドをオブジェクトに追加または削除することはできません。クラスの説明を変更する必要がありますが、これは (意図的に) サポートされていません。

  • var f = o.foo;常に新鮮なクロージャーを取得することを意味します。これは、このバインドされたクロージャをハッシュテーブルのキーとして使用できないことを意味します。たとえば、各register(o.foo)o.foounregister(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 は(これは)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)、実際にはクラス定義のメソッドへの参照を渡しているわけではありません。インスタンスに固有のメソッド「オブジェクト」を渡しますone

ではcallerJustForThree、 instance の同じメソッド「object」を渡します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 に答える