2012-04-09 16 views
8

イベントを割り当てられたコントロールのセットを動的に作成するGUIを作成します。実行時にこれらのコントロールを追加して削除する必要があります。それは次のようになります。追加されたイベントハンドルによるメモリリークを防ぐための予防策

FlowLayoutPanel.Controls.Clear(); 
<< add new controls, assigning Click events with += >> 

私が聞いたこと(よりspecificly、メモリはアプリケーションが終了するまで解放されません)メモリリークが発生する可能性があります+ =とイベントハンドラを割り当てます。私はこれを避けたい。私はすべてのイベントハンドラを見つけて削除するためにここにあるHow to remove all event handlers from a controlのようないくつかの関数を書くことができますが、それは非常に複雑に見えます。

別の方法がありますか? Disposeを呼び出すと、これらのイベントハンドラが削除されますか? C/C++のようにメモリを解放するようにオブジェクトを破棄できますか?

ありがとうございます!

PS:問題は、私はどのイベントを切り離すべきかわかりません。たくさんのラベルを作成し、さまざまな種類のonclickイベントをそれらに追加します。フローレイアウトパネルをクリーニングするときに、どのラベルにどのイベントハンドラがアタッチされたかを知る方法がありません。

これはサンプルコードです(_flowLPはFlowLayoutPanelです)。このRefresh()関数は、アプリケーションが終了する前に複数回実行されています。

private void Refresh() 
    { 
     Label l; 
     Random rnd = new Random(); 

     // What code should i add here to prevent memory leaks 
     _flowLP.Controls.Clear(); 

     l = new Label(); 
     l.Text = "1"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "2"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "3"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "4"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "5"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 

     l = new Label(); 
     l.Text = "6"; 
     if (rnd.Next(3) == 0) l.Click += Method1; 
     if (rnd.Next(3) == 0) l.Click += Method2; 
     if (rnd.Next(3) == 0) l.Click += Method3; 
     _flowLP.Controls.Add(l); 
    } 
+0

編集について:イベントの呼び出しリストに追加されたメソッドを確実に判断できない場合は、イベントハンドラに追加されたデリゲートをコレクションのどこかに残し、そのデリゲートを使用して、来る。または、イベントの呼び出しリストを完全にクリアしていることがわかっている場合は、それらを完全にクリアしてください。 – phoog

答えて

3

これは主に、長寿命のイベントコンシューマに短命のイベントコンシューマを添付するときの心配になります。彼らの生活が似ている、または私が記述したものの反対である場合、それは問題ではありません。

これを心配する場合は、 - =を使用してイベントから切り離すだけです。これにより、添付ファイルによって作成された参照が削除され、この種類のメモリの問題を回避できます。

編集:コメントが少し長くなっているので、私はここにいくつかのフォローアップを投稿します。イベントにアタッチするときは、自分自身にイベントプロバイダの参照をハングアップさせます。たとえば、StrikesMidnightイベントを持つClockクラスがあり、Bedtimeというクラスからそのイベントを購読している場合、clock.StrikesMidnight += this.HandleMidnight;と言っている就寝時間の実際の仕組みは、クロックを自分自身に割り当てることです。それは時計がオブジェクトのプロパティを持っていたかのようです。clock.ObjectProperty = this;

したがって、就寝時間クラスが短命でスコープ外になった場合、就寝時間が表示され、時計で自分自身への参照がハングアップします範囲外。問題は、Clockには依然として参照があるため、スコープ外であっても、ガベージコレクタはBedtimeを収集しません。

....

これはバックグラウンドです。あなたの場合は、ラベルを作成して、自分自身への参照を( "MethodX"ハンドラを介して)それに添付します。リフレッシュが呼び出されると、ラベルのリストが消去されます(ラベルが範囲外になる)。それらは範囲外になり、MethodXハンドラを介してクラスへの参照がありますが、何ですか?参照を持つものであっても、それらがGCされてしまうことはありません。誰もあなたのコードでそれらへの参照を保持しているわけではないので、GCはそれらの作業を行い、メモリをリークしません。

+1

しかし、私はどのイベントを切り離すべきかわかりません!私は何をしますか? – Istrebitel

+1

これは+ =ステートメントでやっていることの逆です。ローカルイベントハンドラを共同作業者のイベントにアタッチするたびに、発生するイベントを認識する必要がなくなったときに、 - =を使用して切り離すことを忘れないでください。これは厳密には必要ではないが、必要に応じてdispose()メソッドで行うことができます。 –

+0

はい、どのメソッドを切り離すかわかりません。たとえば、私は10の方法を持っています。そのうちの1つは、その時点での条件に基づいて、以前は20ラベルの5番目に付けられていました。今、私はflowlayoutpanelからすべてのラベルを削除する必要があります。私は10人のイベントハンドラのどれが私に - =必要なのかをどのように知っていますか? – Istrebitel

0

すべてのコントロールは、含まれているフォームを処理する限り、ガベージコレクタでクリーンアップする必要があります。

コントロールのイベントを購読すると、コントロールが処理委任先への参照を持つため、コントロールは生き残りません。デリゲートにはコントロールへの参照がありません。

イベントサブスクリプションがコントロールのクリーンアップを維持する状況では、コントロール内の一部のコードが、フォームインスタンスの外部にあるイベントにサブスクライブしています。たとえば、カスタムコンボボックス静的クラスのイベントにサブスクライブして、オプションリストの更新時期をコントロールに知らせます。カスタムコントロールがこのイベントを巻き戻さない場合は、アプリケーションの持続時間の間、静的クラスイベントレジスタによって参照されます。コントロールはコンテナへの参照を持ちます(など)ので、フォーム全体がおそらくメモリに残ります。このような場合、コントロールはDisposeメソッドでイベントを展開する必要があります。静的クラスまたは長生きのインスタンスクラスのイベントを登録すると、常に赤いフラグが立てられ、不要になったら明示的に配線する必要があります。

フォームでは、すべてのイベントがフォームクラスのインスタンスメソッドまたはフォームインスタンスによってスコープが制御されているオブジェクトに配線されている限り、フォームがオブジェクトグラフであるときにコントロールがクリーンアップされます根絶は範囲外になる。外部クラスがもはや必要がなくなった後にフォームへの参照を保持しないように注意してください。

+0

つまり、スコープを外れるオブジェクトが別のオブジェクトのメソッドをそのイベントにサブタブすると、そのオブジェクトはそのイベントにサブタブされ、それは破棄され、GCedされますが、スコープ外に出るオブジェクトのメソッドのいくつかは、オブジェクトのイベント、それはGCedされません? – Istrebitel

+0

それはかなり正しいです。イベントを購読しているオブジェクトが収集に適格になると、ガベージコレクションが行われます。そうでなければ収集することができるオブジェクト間の循環参照は、それらが収集されないようにする。 –

0

私の最初の提案は、あらかじめ最適化しないことです。解決する前にこれが問題であることを確認してください。

Dispose()に関して:オブジェクトをガベージコレクションする前に、非管理リソースを解放するのにのみを使用する必要があります。詳細については、http://msdn.microsoft.com/en-us/library/system.idisposable.aspxを参照してください。

イベントハンドラがコントロールへの参照を保持しないようにするには、コントロールの作成時に登録したすべてのイベントハンドラからのコントロールの登録を解除する必要があります。ヒント:GUIからイベントハンドラを追加する場合は、自動生成コードを調べて、FlowLayoutPanel.Controls.Clear()を呼び出す前に、退会する必要があるものを確認します。これを行うきれいな(-er?)方法は、目的のコントロールから継承する独自のコントロールを作成し、サブスクライブされているイベントハンドラのサブスクライブを解除する `Cleanup() 'メソッドを追加することです(どのイベントコードを書いたか、それが生成されたためにハンドラが登録されています)。

+0

いいえ、私が言ったように、私は動的にそうしているので、GUIではありません。 – Istrebitel

関連する問題