8

イベントが割り当てられた一連のコントロールを動的に作成する GUI を作成します。実行時にこれらのコントロールを追加および削除する必要があります。次のようになります。

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

+= を使用してイベント ハンドラーを割り当てると、メモリ リークが発生する可能性があると聞きました (具体的には、アプリケーションが終了するまでメモリは解放されません)。これは避けたい。私はここのようないくつかの関数を書くことができることを知っています コントロールからすべてのイベントハンドラーを削除してすべてのイベントハンドラーを見つけてそれらを削除する方法は非常に複雑に見えます。

別の方法はありますか?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);
    }
4

3 に答える 3

3

これは主に、存続期間の短いイベント コンシューマーを存続期間の長いイベント プロデューサーにアタッチするときに心配になります。彼らが似たような生活を送っている場合、または私が説明したものとは反対である場合、それは問題ではありません.

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

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

そのため、Bedtime クラスが存続期間が短く、スコープ外になった場合、Bedtime が表示され、それ自体への参照が Clock にぶら下がり、スコープ外になります。問題は、Clock がまだそれへの参照を持っているため、スコープ外であっても Garbage Collector が Bedtime を収集しないことです。

....

それが背景です。あなたの場合、ラベルを作成し、自分自身への参照を(「MethodX」ハンドラーを介して)それに添付しています。refresh が呼び出されると、ラベルのリストをクリアします (ラベルが範囲外になることを意味します)。それらは範囲外になり、MethodX ハンドラーを介してクラスへの参照を持っていますが、それでどうなるでしょうか? それらが参照を持っていても、それらが GC されるのを妨げません。コード内で誰もそれらへの参照を保持していないため、GC がそれらに対して作業を行い、メモリ リークは発生しません。

于 2012-04-09T18:06:34.943 に答える
0

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

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

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

于 2012-04-09T20:06:15.650 に答える
0

含まれているフォームを破棄する限り、すべてのコントロールがガベージ コレクターによってクリーンアップされるはずです。

コントロールは処理デリゲートへの参照を持っているため、コントロールのイベントをサブスクライブしても、コントロールは存続しません。デリゲートにはコントロールへの参照がありません。

イベント サブスクリプションによってコントロールがクリーンアップされない状況は、コントロール内の一部のコードが、それが含まれているフォーム インスタンスの外部でイベントをサブスクライブしている場合です。たとえば、カスタム コンボ ボックスがイベントをサブスクライブしている場合静的クラスで、オプション リストをいつ更新する必要があるかをコントロールに知らせます。カスタム コントロールがこのイベントの配線を解除しない場合、アプリケーションの期間中、静的クラス イベント レジスタによって参照されます。コントロールはそのコンテナー (など) への参照を持つため、フォーム全体がおそらくメモリに残ります。このような場合、コントロールはその Dispose メソッドでイベントを配線解除する必要があります。静的クラスまたは長寿命のインスタンス クラスのイベントをサブスクライブすると、常に危険信号が発生し、不要になったときに明示的に配線を解除する必要があります。

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

于 2012-04-09T19:02:37.367 に答える