4

私には次の機能があります。

コントロール(おそらくWindowsフォーム)が与えられた場合、ルール(必要なコントロールをスクリーニングする関数)に「従う」すべてのコントロールイベント(KeyDownなど)にサブスクライブさせたいと考えています。

問題は、どうすれば退会できますか?またはもっと重要なことに、私はする必要がありますか?

フォーム自体のフォームのLoadイベントでこれを使用するので、フォームが閉じた場合、本当に登録を解除する必要がありますか?

(少し読んでGCを少し理解した後、購読を解除する必要はないと思いますが、よくわかりません)

//an example of using the function
    private void Form1_Load(object sender, EventArgs e)
    {
        MyEventHandler.CreateKeyDownEventHandlers(this);
    }

//the function
    public static void CreateEventHandlers(Control Ctrl)
    {
        foreach (Control c in Ctrl.Controls)
        {
            //bool Rules(Control) a function that determines to what controls'
            //events to apply the handler 
            if ( Rules(c) )
            {
                c.KeyDown += (s, e) =>
                {
                  // do something
                };

            }

            //a control might be a groupbox so we want their contained
            //controls also
            if (c.Controls != null)
            {
                if (c.Controls.Count > 0)
                {
                    CreateEventHandlers(c);
                }
            }

        }
    }

これは、イベント、デリゲート、無名関数、ラムダを使った最初の試みなので、本当に愚かなことをした場合は教えてください。

4

3 に答える 3

2

まず、無名関数がハンドラー変数に割り当てられ、その変数がイベントに追加されてから削除されない限り、無名関数のサブスクライブを解除することはできないと思います。

登録を解除する必要があるかどうか:オブジェクトの有効期間について考えてください。静的メソッドで無名関数を作成し、ライフタイムを制御すると想定しているコントロールにアタッチします。

これらのコントロールの1つを破棄すると、それらは匿名関数を参照しなくなり、GCはそれら(匿名関数)を強制終了できるため、サブスクライブを解除する必要はありません。

状況が逆転し、静的メソッドで作成されたものがコントロールを参照した場合、静的コンテキストのイベントにコントロールデリゲートが追加されたかのように、GCは、コントロールへの参照が行われるまで、コントロールを処理できませんでした。削除されました。これは、静的メソッドで実行された場合は発生しません。

于 2010-01-21T10:50:20.020 に答える
2

フォームを1回作成し、これらのハンドラーも最初に1回作成する場合は、実際には何もクリーンアップする必要はありません。

ただし、複数回作成する場合(たとえば、ユーザーがボタンをクリックしたときにフォームを何度も作成する場合)は、注意が必要です。そして、ここでの答えは、ハンドラーに正確に何があるかによって異なります。

c.KeyDown += (s, e) =>
            {
              // do something
            };

一般に、デリゲートをイベントに割り当てると、GCの観点から依存関係サイクルが発生する可能性があります。たとえば、フォームにコントロールAが含まれ、Aのイベントに登録されると想像してください。その後、フォームはAが破棄されるまで破棄できず、Aは破棄できません。フォームが破棄されるまで破棄されます(コールバックを介して間接的にフォームを参照するため)。コントロールAと一緒にフォームを作成するだけの場合は問題ありませんが(GCは両方を同時に削除します)、コントロールAを動的に作成すると、メモリリークが発生する可能性があります。

于 2010-01-21T11:03:56.650 に答える
0

を使用してイベントの購読を解除できます

yourobject.Yourevent-= YourSubscribedFunction;

これにより、この関数のサブスクライブがイベントから解除されます。

あなたの質問の2番目の部分について:

イベントを含むオブジェクトが破棄された場合でも、サブスクライブを解除する必要はありません。

サブスクライブしているオブジェクトが破棄された場合はどうなるかわかりませんが、テストでは、オブジェクトが存在しなくなっても、関数がまだ呼び出されていることが示されています。

ClassA a = new ClassA();
using (ClassB b = new ClassB()) // implements IDisposable
{
    b.SubscribeToFoo(a); // b subscribes to FooEvent of ClassA
    a.DoFoo(); // a executes FooEvent
}
GC.Collect(); // Run Garbage Collector just to be sure
a.DoFoo(); // a executes FooEvent

bが破棄されても、ClassBのサブスクライブされたメソッドが呼び出されます。

于 2010-01-21T10:51:23.520 に答える