1

WinForms では、IDisposable 実装を使用してフォーム イベント (Activated、Load、ContextMenuChanged など) のサブスクライブを解除し、ガベージ コレクションを支援できますか?


MSDNでの購読解除

イベントが発生したときにイベント ハンドラーが呼び出されないようにするには、イベントのサブスクライブを解除します。リソース リークを防ぐために、サブスクライバー オブジェクトを破棄する前に、イベントからサブスクライブを解除する必要があります。イベントのサブスクライブを解除するまで、発行オブジェクトのイベントの基になるマルチキャスト デリゲートは、サブスクライバーのイベント ハンドラーをカプセル化するデリゲートへの参照を保持します。パブリッシング オブジェクトがその参照を保持している限り、ガベージ コレクションはサブスクライバー オブジェクトを削除しません。

4

4 に答える 4

4

はい、できますが、イベントの数によっては、これはマイクロ最適化のカテゴリに分類されると思います。

于 2013-09-17T12:35:52.873 に答える
3

はい、可能ですが、イベントハンドラが独自のクラスで定義され、同じインスタンスでも定義されている場合は、パブリッシャーとサブスクライバーが同じオブジェクトであるため、イベントからサブスクライブを解除する必要はありません。したがって、余分なオブジェクトが参照に保持されることはありません。

オブジェクト A をサブスクライブしてオブジェクト B のイベントを処理する場合は、オブジェクト B のイベントをサブスクライブ解除する価値があります。そうしないと、イベントの基礎となるマルチキャスト デリゲートが両方のオブジェクトへの参照を保持します。これにより、ガベージ コレクターが両方のオブジェクトを収集できなくなります

于 2013-09-17T12:41:45.590 に答える
1

通常の WinForms のユース ケースでは、パブリッシャーとサブスクライバーがくっついて同時に破棄されます。ボタンの OnClick イベントへのサブスクリプションは、通常、ボタンを含むウィンドウ クラスのメソッドです。ボタンを削除せずにメモリからウィンドウを削除しても意味がありません。

このような場合、購読を解除する必要はありません (私が知る限り)。

ウィンドウの OnLoad に反応する別のウィンドウなど、パブリッシャーが処理する前にサブスクライバー クラスが破棄される場合にのみ問題になります。次に、IDisposable を使用することをお勧めします。

于 2013-09-17T13:05:29.250 に答える
1

@Maarten の answerに追加するには、「独自の」イベントを a 内で処理するのではなく、通常、それらのイベントを呼び出すForm多数のメソッドをオーバーライドする方がはるかに簡単です。protected virtual

Loadつまり、イベントに添付する代わりに:

this.Load += DoStuff;

private void DoStuff(object sender, EventArgs e) 
{
    // do stuff
}

メソッドをオーバーライドするだけOnLoadで、登録解除について考える必要がまったくなくなります。

protected override void OnLoad(EventArgs e)
{
    // do stuff
    ...

    // call the base method to fire the event 
    // for external listeners
    base.OnLoad(e);
}  

これにより、外部オブジェクトのイベントのみのハンドラーが残ります。これらのハンドラーは、使用が終了したら切り離す必要があります。

これが、パブリック イベントごとに常にprotected virtual OnXXXXメソッドを用意することをお勧めする理由でもあります。つまり、派生クラスがイベントを発生させ、イベントを実行する前に追加の処理ロジックを追加できるようにするためです。

于 2013-09-17T12:46:43.257 に答える