ランタイム中にアプリケーション内で作成および破棄されるUserControls
を使用します(内部でこれらのコントロールを使用してサブウィンドウを作成および閉じます)。
これはWPF UserControlであり、_System.Windows.Controls.UserControl
_を継承しています。オーバーライドできるDispose()
メソッドはありません。PPMM
は、私のアプリケーションと同じ存続期間を持つSingleton
です。
(WPF)UserControl
のコンストラクターで、イベントハンドラーを追加します。
_public MyControl()
{
InitializeComponent();
// hook up to an event
PPMM.FactorChanged += new ppmmEventHandler(PPMM_FactorChanged);
}
_
デストラクタでそのようなイベントハンドラを削除することに慣れました:
_~MyControl()
{
// hook off of the event
PPMM.FactorChanged -= new ppmmEventHandler(PPMM_FactorChanged);
}
_
今日、私はこれにつまずいて、疑問に思いました:
1)これは必要ですか?または、GCがそれを処理しますか?
2)これでも機能しますか?または、新しく作成されたppmmEventHandler
を保存する必要がありますか?
あなたの答えを楽しみにしています。
PPMM
は長命オブジェクト(シングルトン)であるため、このコードはあまり意味がありません。
ここでの問題は、そのイベントハンドラがオブジェクトを参照している限り、ガベージコレクションの対象にならないことです。イベントを所有するオブジェクトは生きています。
そのため、デストラクタに何かを入れるのは無意味です。
要するに、これをしないでください。
さて、Dispose
を実装しているときに、そのようなコードをIDisposable
メソッドに追加することについて、別の引数を言うことができます。 thatの場合、Dispose
を呼び出しているユーザーコードは、事前定義され制御されたポイントであるため、完全に意味があります。
ただし、ファイナライザ(デストラクタ)は、オブジェクトがガベージコレクションの対象であり、ファイナライザを持っている場合にのみ呼び出されます。
質問はnbr。 2、私は「そのようなイベントから退会できますか?」サブスクライブに使用したデリゲートを保持する必要があるのは、匿名メソッドまたはラムダ式を中心にデリゲートを構築しているときだけです。既存のメソッドを中心に構築すると、機能します。
編集:WPF。そのタグは見えませんでした。申し訳ありませんが、私の答えの残りの部分はWPFにとってあまり意味がありません。また、WPFの第一人者ではないので、本当に言えません。ただし、これを修正する方法があります。ここでは、SOで、他の回答の内容を改善することができれば、それを密かに入手することができます。私の答えの最初のセクションとWPFの関連ビットを追加します。
編集:ここにあるコメントの質問にも答えさせてください。
問題のクラスはユーザーコントロールであるため、その有効期間はフォームに関連付けられます。フォームが閉じられると、フォームが所有するすべての子コントロールを破棄します。つまり、既にDisposeメソッドが存在します。
ユーザーコントロールが独自のイベントを管理している場合、これを処理する正しい方法は、Disposeメソッドでイベントハンドラーをアンフックすることです。
(休憩を外した)
WPFはIDisposable
をサポートしていません。クリーンアップが必要なWPFコントロールを実装する場合は、代わりに Loaded
および Unloaded
イベントへのフックを検討する必要があります(または加えて)。
つまりLoaded
ハンドラーでイベントに接続し、Unloaded
ハンドラーで切断します。もちろん、これは、コントロールが「ロード」されていない間にイベントを受信する必要がなく、多くのロード/アンロードサイクルを正しくサポートできる場合のみのオプションです。
Loaded
/Unloaded
イベントを使用する利点は、ユーザーコントロールを使用するすべての場所に手動で配置する必要がないことです。ただし、アプリケーションのシャットダウンが開始された後、Unloaded
イベントは発生しないことに注意してください。例えば。シャットダウンモードがOnMainWindowClose
の場合、他のウィンドウのUnloaded
イベントは発生しません。これは通常、問題ではありません。これは、アプリケーションが終了する前/間に発生する必要があるUnloaded
で確実に行うことができないことを意味します。
まず、destructorを使用せず、Dispose()を使用してリソースをクリアすると言います。
第二に、私の意見では、このコードが非常に頻繁に作成され、寿命が短いオブジェクト内にある場合、take careとしてイベントハンドラを自分で削除する方が良いこれはホルダーオブジェクトへのリンクで、prevent[〜#〜] gc [〜#〜] from収集中。
よろしく。
コードがデストラクタに到達した場合、それはもう問題ではありません。
これは、イベントをリッスンしていない場合にのみ破棄されるためです。
イベントをまだリッスンしている場合、破壊されていません。
PPMM
は、MyControl
インスタンスよりも寿命が長い外部のものですか?
その場合、PPMM_FactorChanged
は静的メソッドです。ppmmEventHandler
はMyControl
インスタンスへの参照をライブで保持します。つまり、インスタンスはガベージコレクションの対象にならず、ファイナライザは起動しません。
削除コードのためにppmmEventHandler
を保持する必要はありません。
GCがそれを処理します。イベントは強い参照を保持しますが、親オブジェクト自体でのみ保持します。最終的に、MyControlのみがイベントハンドラーを介して参照を保持するため、GCが参照を収集します。
一方、ファイナライザを使用しますが、これはスクラクタではありません。この悪い習慣のためです。イベントの登録を解除する場合は、IDisposable
を検討する必要があります。
イベントハンドラは扱いにくいため、リソースリークを簡単に隠すことができます。ティグランが言うように。 IDisposeableを使用し、デストラクタを忘れます。正しいかどうかを測定することをお勧めします。タスクマネージャーでアプリのメモリ消費量を調べるだけで、数千のウィンドウをロードして閉じてストレステストを少し行うと、リークがあるかどうかがわかります。
ファイナライザ/デストラクタでのイベントのサブスクライブ解除が役立つ場合がありますイベントの発行者がサブスクライブ解除がスレッドセーフであることを保証した場合。 ownイベントからサブスクライブ解除するオブジェクトは役に立たないでしょうが、実行可能なパターンとして、パブリックに面したオブジェクトに、実際に「すべての作業を行う」プライベートオブジェクトへの参照を保持させることができます。そのプライベートオブジェクトにイベントをサブスクライブさせます。プライベートオブジェクトからパブリックオブジェクトへの参照が存在しない場合、パブリックオブジェクトはまだ誰も関心を持たない場合、ファイナライズの対象になります。そのファイナライザは、プライベートオブジェクトに代わってサブスクリプションをキャンセルできます。
残念なことに、このパターンは、イベントがサブスクライブされたコンテキストだけでなく、イベントがサブスクライブされているオブジェクトがスレッドコンテキストからのサブスクライブ解除要求を受け入れることが保証されている場合にのみ機能します。 .NETが「イベント」コントラクトの一部として、すべてのサブスクライブ解除メソッドがスレッドセーフでなければならないことを要求しても、実装に大きな負担はかかりませんでしたが、何らかの理由でMSはそのような要求を課しませんでした。したがって、ファイナライザ/デストラクタがイベントをできるだけ早くサブスクライブ解除する必要があることを発見した場合でも、それを実現するための標準的なメカニズムはありません。
2)これは動作します
1)(アプリ内メッセージングサービスで)グローバルオブジェクトへのイベントハンドラーが解放されず、GCがそのためにオブジェクトを収集できなかった場合がありました。これは通常、まれな状態だと思います-赤いゲートのANTSのようなプロファイラーを使用すると、これがあなたに起こると思う場合、簡単にメモリプロファイリングを行うことができます。