ラムダをイベント ハンドラーとして使用するとメモリ リークが発生する可能性がありますか?

StackOverflow https://stackoverflow.com/questions/16473

質問

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

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?

役に立ちましたか?

解決

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

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

そして、はい、そうしなければ、そうするでしょう リーク 毎回新しいデリゲート オブジェクトを接続することになるため、メモリが必要になります。また、このメソッドを呼び出すたびに、コンソールに出力される行数が増加するため (単に数が増加するだけではなく、MethodThatFiresAnEvent を 1 回呼び出すと、任意の数の項目が 1 回ずつダンプされるため)、このことにも気づくでしょう。それぞれが接続された匿名メソッド)。

他のヒント

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

まあ、これまでに行われたことを拡張することもできます ここ デリゲートをより安全に使用できるようにする (メモリ リークがない)

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

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

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

もちろん、へのアクセスが必要になります。 マイオブジェクト クラスのプライベートメンバー。

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

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

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

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top