2009-04-21 14 views
16

オブジェクト内のイベントにアタッチすることの意味が完全にわかっているかどうかはわかりません。どのような場合にイベントから切り離す必要がありますか?

これは正しい、私の現在の理解であるか詳しく説明:

this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);

public event EventHandler OnMyCustomEvent = delegate { };

:ローカルクラスのイベントにアタッチ

1.

例をデタッチする必要はありません。

私はあなたのobまたは削除された場合、関数は割り当てが解除され、自動的にイベントから切り離されます。 はあなただけに一度に応答タイマーのElapsedイベントにアタッチ:;あなた不要になった(=ヌル)オブジェクトへの接続

2.

例から取り外すことがあります。イベントが発生した後にElapsedイベントをデタッチできるように、ローカル変数にTimerを格納する必要があると仮定します。したがって、その漏れにつながるようにローカルメソッドのスコープでタイマーを宣言:

System.Timers.Timer myDataTimer = new System.Timers.Timer(1000); myDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(myDataTimer_Elapsed);

3を配置する必要はありませんあなたのクラスにローカルオブジェクトでのイベントにアタッチ?

例えば、ObservableCollectionを作成し、監視し、終了させるObservableCollectionがあったとします。ローカルのプライベート関数を使用してCollectionChangedイベントにアタッチした場合、クラスがガベージコレクションされたときにこの関数の割り当てが解除され、ObservableCollectionも解放されますか?

私はオブジェクトの使用を中止した場所があり、イベントから切り離されていないと確信しています(たとえば、私が作ったタイマーの例)。これはどういう仕組みでより明確な説明を探しています。

答えて

22

私はあなたが必要以上に複雑になっていると思います。

  • イベントに登録すると、イベントの「所有者」(パブリッシャー)は通常、登録したデリゲートへの参照を保持します。
  • デリゲートのアクションとしてインスタンスメソッドを使用する場合、デリゲートには「ターゲット」オブジェクトへの参照があります。

これは、あなたが書いた場合ことを意味します

publisher.SomeEvent += subscriber.SomeMethod; 

後で解除しない限り、publisherがある前に、次にsubscriberがガベージコレクションの対象にはなりません。多くの場合、subscriberがちょうどthisある

注:それがインスタンスメソッドの仮定

publisher.SomeEvent += this.myDataTimer_Elapsed; 

publisher.SomeEvent += myDataTimer_Elapsed; 

は、と等価です。

はありませんイベントのサブスクリプションのみに起因する逆の関係です。つまり、サブスクライバはパブリッシャを存続させません。

詳細については、my article on events and delegatesを参照してください。

+0

あなたは正しいです、私は事を複雑にしています。例を探す際には、そのほとんどがイベントから切り離されていただけで、購読者がサイト運営者を存続させることができると私は信じました。 –

+0

愚かな質問:それは、パブリッシャーがサブスクライバのイベントにサブスクライブすると、その2つのどちらも収集できないことを意味しますか? –

+2

いいえ、循環参照があることを意味します。どちらにも*他の*根拠のない参照がなくなると、それらは両方ともGCの対象となります。 –

1

あなたがイベントから退会する必要が該当する場合は、このようなものです:このシナリオでは

public class A 
{ 
    // ... 
    public event EventHandler SomethingHappened; 
} 

public class B 
{ 
    private void DoSomething() { /* ... */ } // instance method 

    private void Attach(A obj) 
    { 
     obj.SomethingHappened += DoSomething(); 
    } 
} 

あなたはBを廃棄する場合、まだobj年代からそれへのダングリング参照が存在しますイベントハンドラ。 Bのメモリを再利用する場合は、最初に関連するイベントハンドラからB.DoSomething()を切り離す必要があります。

イベントサブスクリプションの行はこのように見えた場合はもちろん、同じことに遭遇することができます:

obj.SomethingHappened += someOtherObject.Whatever.DoSomething(); 

は、今ではフックの上だとゴミを収集することはできませんsomeOtherObjectです。

3

ガベージコレクションを防止する残りの参照には、明白かもしれないがまだこのスレッドでは述べられていないもう1つの効果があります。添付されたイベントハンドラも同様に実行されます。

私はこれを数回経験しました。 1つは、アプリケーションが徐々に遅くなり、実行時間が長くなるにつれて遅くなるというものでした。アプリケーションは、ユーザーコントロールを読み込むことによって動的にユーザーインターフェイスを作成しました。コンテナは、ユーザーコントロールが環境内の特定のイベントを購読するようにしました。そのうちの1つは、コントロールが「アンロード」されたときに登録解除されませんでした。

これにより、特定のイベントが発生するたびに多数のイベントリスナーが実行されました。これはもちろん、睡眠の良い数が突然起きて同じ入力に作用しようとすると、深刻な競争状態につながる可能性があります。

要するに、イベントリスナーをフックアップするコードを書く場合それがもはや必要でないとすぐにあなたが解放することを確認してください。私は、それが将来、ある時点で少なくとも1つの頭痛からあなたを救うことを約束することを敢えて思います。

関連する問題