1

これは、WinForm MDI アプリケーションの問題 (.net Framework 3.0) です。C#で記述します。できるだけわかりやすくしようとしているので、少し長くなってしまい申し訳ありません。

MDI アプリケーションがあります。ある時点で、1 つの MDI 子フォームがリリースされないことに気付きました。MDI 子フォームを作成して表示するメニューがあります。MDI 子フォームが閉じられると、それは破棄され、使用されたメモリは .net に戻されるはずです。しかし、驚いたことに、これは真実ではありません。すべての MDI 子フォーム インスタンスはメモリに保持されます。これは明らかに「メモリリーク」です。まあ、それは .net の実際のリークではありません。閉じたフォームは死んでいるはずだと思うだけですが、どういうわけか、閉じたフォームにまだ接続している外の世界からの未知の参照が少なくとも1つあります.

Web でいくつかの記事を読みました。MDI 子フォームが閉じているときに、すべてのイベント ハンドラーの配線を解除する必要があると言う人もいます。そうしないと、一部のイベント ハンドラーがフォームを存続させてしまう可能性があります。フォームを閉じる前に DataBindings を消去する必要があると言う人もいます。そうしないと、DataBindings がグローバル Hashtable への参照を追加して、フォームを存続させます。

私のフォームにはかなり多くのものが含まれています。多くのイベント ハンドラーと多くの DataBinding と多くの BindingSource と、ユーザー コントロールと HelpProvider を含む疑いのあるコントロールがいくつかあります。関連するすべてのコントロールからすべてのイベント ハンドラーを切り離し、すべての DataBinding と DataSource をクリアする大きなメソッドを作成します。HelpProvider コントロールとユーザー コントロールは慎重に破棄されます。

最後に、DataBinding と DataSource をクリアする必要がないことがわかりました。イベント ハンドラーが問題の原因であることは間違いありません。そしてMDIのフォーム構造も何かに貢献しています。

実験中に、MDI 子フォームを作成すると、それを閉じても、メモリ内に 1 つのインスタンスが残ることがわかりました。参照はメイン フォームの PropertyStore からです。これは、メイン フォームが閉じられない限り (アプリケーションが終了しない限り)、メモリ内に MDI 子フォームのインスタンスが常に 1 つ存在することを意味します。幸いなことに、子フォームを何度開いたり閉じたりしても、インスタンスは 1 つだけで、大きな "リーク" は発生しません。

イベント ハンドラーになると、事態はさらに複雑になります。これに対処する必要があります。私のフォームのすべてのイベント ハンドラーは、匿名のイベント ハンドラーです。コード例を次に示します。

//On MDI child form's design code...

Button btnSave = new Button(); 

btnSave.Click += new System.EventHandler(btnSave_Click);

WherebtnSave_Clickも MDI 子フォームのメソッドです。上記は、さまざまなコントロールとさまざまな種類のイベントに常に当てはまります。私にとって、これは双方向の循環参照です。btnSave は、イベント ハンドラーを介して MDI 子フォームの参照を保持します。MDI 子フォームは、btnSave インスタンスの参照を保持します。繰り返しになりますが、このような双方向の循環参照は、.net のガベージ コレクターに問題を引き起こすことはありません。これは、フォームが破棄されているときにイベントを明示的に配線解除する必要がないことを意味します。

btnSave.Click -= btnSave_Click;

しかし、真実はそうではありません。一部のイベント ハンドラについては、安全です。それらを無視しても、インスタンスが重複することはありません。他のいくつかのイベント ハンドラーでは、1 つのインスタンスがメモリに残ります (MDI フォーム構造と同様の効果ですが、今回はイベント ハンドラーのハングが原因です)。他の一部のイベント ハンドラーでは、メモリ内ですべてのインスタンスが開かれます。これら 3 種類のイベント ハンドラの違いについて、私は完全に混乱しています。コントロールは同じ方法で作成され、イベントは同じ方法で関連付けられます。違いはなんですか?(違いを生むのはイベント処理メソッドだとは言わないでください。)この有線シナリオの経験があり、私に答えがある人はいますか?どうもありがとう。

したがって、安全上の問題から、フォームが破棄されるときにすべてのイベント ハンドラーの配線を解除する必要があります。これは、各コントロールの同様のコードの長いリストになります。リフレクションを使用して再帰的にコントロールからイベントを削除する一般的な方法はありますか? パフォーマンスの問題はどうですか?

これで私の話は終わりです。私はまだ問題の真っ最中です。助けていただきありがとうございます。

4

1 に答える 1

0

イベントハンドラーが接続されているオブジェクトが子フォームで宣言されている限り、子フォームが破棄されるときにイベントハンドラーを削除する必要はありませんが、イベントハンドラーが接続されているオブジェクトが外部で宣言されている場合子フォーム子フォームを破棄するときに、イベントハンドラーを削除する必要があります。

このコードが複数回実行された場合(およびbtnSaveが子フォームで宣言されていないオブジェクトである場合)

btnSave.Click += btnSave_Click;

このコードは同じ回数実行する必要があります

btnSave.Click -= btnSave_Click;


アプリケーションのどこかで子フォームを参照している可能性があります。ガベージコレクターが子フォームオブジェクトを削除する前に、この参照を削除する必要があります。

于 2009-12-28T14:22:06.340 に答える