20

次のメソッドがあるとします。

private MyObject foo = new MyObject();

// and later in the class

public void PotentialMemoryLeaker(){
  int firedCount = 0;
  foo.AnEvent += (o,e) => { firedCount++;Console.Write(firedCount);};
  foo.MethodThatFiresAnEvent();
}

このメソッドを持つクラスがインスタンス化され、PotentialMemoryLeakerメソッドが複数回呼び出された場合、メモリ リークが発生しますか?

の呼び出しが完了した後、そのラムダ イベント ハンドラーをアンフックする方法はありますMethodThatFiresAnEventか?

4

5 に答える 5

16

はい、変数に保存してフックを解除します。

DelegateType evt = (o, e) => { firedCount++; Console.Write(firedCount); };
foo.AnEvent += evt;
foo.MethodThatFiresAnEvent();
foo.AnEvent -= evt;

そうしないと、毎回新しいデリゲート オブジェクトを接続するため、メモリリークが発生します。また、このメソッドを呼び出すたびに、増加する行数がコンソールにダンプされるため、これにも気付くでしょう (増加する数だけでなく、MethodThatFiresAnEvent への 1 回の呼び出しで、任意の数のアイテムが一度にダンプされます)。それぞれが接続された匿名メソッド)。

于 2008-08-19T16:08:24.663 に答える
4

メモリをリークするだけでなく、ラムダが複数回呼び出されます。「PotentialMemoryLeaker」を呼び出すたびに、ラムダの別のコピーがイベント リストに追加され、「AnEvent」が発生したときにすべてのコピーが呼び出されます。

于 2008-08-19T16:11:30.227 に答える
3

ここで行われたことを拡張して、デリゲートをより安全に使用できるようにすることができます (メモリ リークはありません)。

于 2008-08-19T16:31:26.020 に答える
2

あなたの例は、コンパイラで名前が付けられたプライベート内部クラスにコンパイルされるだけです(フィールドfiredCountとコンパイラで名前が付けられたメソッドを使用)。PotentialMemoryLeaker を呼び出すたびに、クロージャー クラスの新しいインスタンスが作成されます。ここで、foo は単一のメソッドへのデリゲートを介して参照を保持します。

PotentialMemoryLeaker を所有するオブジェクト全体を参照しない場合、それはすべてガベージ コレクトされます。それ以外の場合は、次のように記述して、 fooを null に設定するか、foo のイベント ハンドラー リストを空にすることができます。

foreach (var handler in AnEvent.GetInvocationList()) AnEvent -= handler;

もちろん、MyObjectクラスのプライベート メンバーにアクセスする必要があります。

于 2008-08-19T16:19:24.453 に答える
0

はい、通常のイベント ハンドラーがリークを引き起こすのと同じように。ラムダは実際には次のように変更されるためです。

someobject.SomeEvent += () => ...;
someobject.SomeEvent += delegate () {
    ...
};

// unhook
Action del = () => ...;
someobject.SomeEvent += del;
someobject.SomeEvent -= del;

つまり、基本的には、私たちが 2.0 で長年使用してきたものの略です。

于 2008-08-19T16:09:37.283 に答える