次の方法で共有


弱いイベント パターン

アプリケーションでは、イベント ソースにアタッチされているハンドラーが、このハンドラーをソースにアタッチしたリスナー オブジェクトとの関連によって、破棄されないことがあります。 このような状況は、メモリ リークにつながる可能性があります。 Windows Presentation Foundation (WPF) では、デザイン パターンが導入されており、このデザイン パターンを使用して、特定のイベントの専用マネージャー クラスを提供し、そのイベントのリスナーにインターフェイスを実装することによって、この問題に対処できます。 このデザイン パターンは、弱いイベント パターンと呼ばれます。

弱いイベント パターンを実装する理由

イベントのリッスンによって、メモリ リークが発生する可能性があります。 イベントをリッスンする場合の一般的な手法は、言語固有の構文を使用して、ソース上でイベントにハンドラーをアタッチすることです。 たとえば、C# では、その構文は source.SomeEvent += new SomeEventHandler(MyEventHandler) です。

この手法では、イベント ソースからイベント リスナーへの強い参照を作成します。 通常、リスナーのイベント ハンドラーをアタッチすると、リスナーはオブジェクトの有効期間を持ちますが、この有効期間はソースのオブジェクトの有効期間の影響を受けます (イベント ハンドラーが明示的に削除されない場合)。 ただし、特定の状況では、リスナーのオブジェクトの有効期間を、ソースの有効期間によってではなく、アプリケーションのビジュアル ツリーに現在属しているかどうかなどの別の要素によって制御したい場合もあります。 ソース オブジェクトの有効期間がリスナーのオブジェクトの有効期間を超える場合、常に通常のイベント パターンによってメモリ リークが発生します。つまり、このリスナーは想定した以上に長く維持されます。

弱いイベント パターンは、このメモリ リークの問題を解決するように設計されています。 弱いイベント パターンは、リスナーをイベントに登録する必要がある際に、いつ登録を解除するかを明示的には認識できない場合に使用できます。 また、弱いイベント パターンは、ソースのオブジェクトの有効期間がリスナーの有用なオブジェクトの有効期間を超える場合にも使用できます (この場合、"有用な" ものは自分自身で判断します)。 弱いイベント パターンを使用すると、リスナーのオブジェクトの有効期間の特性にまったく影響を与えることなく、リスナーをイベントに登録し、イベントを受け取ることができます。 実際には、ソースからの暗黙的な参照では、リスナーがガベージ コレクションの対象であるかどうかは判断されません。 この参照は弱い参照であり、このことから弱いイベント パターンおよび関連する APIs の名前が付けられています。 リスナーはガベージ コレクトされ、そうでなければ破棄されて、ソースは存続することができ、破棄されたオブジェクトへの収集不能なハンドラー参照を継続することもありません。

だれが弱いイベント パターンを実装するのか

弱いイベント パターンの実装は、主にコントロール作成者にとって意味があります。 コントロールの作成者には、作成したコントロールの動作と格納およびそれを組み込むアプリケーションに与える影響に対する主な責任があります。 これには、コントロール オブジェクトの有効期間の動作 (特に、上に示したメモリ リークの問題への対応) が含まれます。

弱いイベント パターンを適用すると本質的に役立つシナリオがいくつかあります。 このようなシナリオの 1 つが、データ バインディングです。 データ バインディングでは、ソース オブジェクトが、バインディングのターゲットであるリスナー オブジェクトにまったく依存しない場合が一般的です。 WPF データ バインディングの多くの側面は、イベントの実装方法に適用される弱いイベント パターンをあらかじめ含んでいます。

弱いイベント パターンを実装する方法

弱いイベント パターンの実装には、次の 3 つの側面があります。

  • マネージャーを WeakEventManager クラスから派生させます。

  • IWeakEventListener インターフェイスを、ソースへの強い参照を生成せずに、弱いイベントのリスナーを登録するすべてのクラスに実装します。

  • リスナーの登録時に、リスナーでパターンを使用する場合は、イベントの通常の追加アクセサーと削除アクセサーを使用しないでください。 代わりに、そのイベントの専用 WeakEventManager での AddListener 実装と RemoveListener 実装を使用します。

WeakEventManager

弱いイベント パターンを実装するには、通常、マネージャー クラスをイベントと 1 対 1 の関係で作成します。 たとえば、Spin という名前の 1 つのイベントがある場合、このイベント専用の弱いイベント マネージャーである 1 つの SpinEventManager クラスを作成します。 このイベントが複数のクラスにあり、一般に各クラスで同様に動作し、同じイベント データ型を共有する場合は、各イベントに同じマネージャーを使用できます。

WeakEventManager クラスから派生する場合は、2 つの仮想メソッドをオーバーライドし、仮想テンプレートによって明確には制御されないにもかかわらず存在するいくつかの他のメンバーを公開します。 オーバーライドは、イベント配信モードを開始または終了するために、WPF インフラストラクチャによって使用されます。 他のメンバーは機能を提供し、これによって、独自の IWeakEventListener 実装は WeakEventManager を使用して、イベントにリスナーをアタッチできます。

WeakEventManager からの派生に関する詳細については、WeakEventManager のリファレンス トピックにある「継承時の注意」セクションを参照してください。

IWeakEventListener

IWeakEventListener インターフェイスでは、ReceiveWeakEvent という名前の 1 つのインターフェイス メソッドが使用されます。 ReceiveWeakEvent の実装は、そのクラスに存在するすべてのイベントの参照を、適切な WeakEventManager に設定する一元的な実装である必要があります。

IWeakEventListener インターフェイスの実装に関する詳細については、ReceiveWeakEvent メソッドのリファレンス トピックにある「実装時の注意」セクションを参照してください。

リスナーのアタッチ

通常のイベントの ClockwiseSpin イベント (Spinner 型によって定義) があるとします。 リスナーとなる SpinListener のリスナー クラスがある場合、ハンドラーをアタッチする通常の手法 (弱いイベント パターンを使用しない) は、次に示す += 演算子の使用です。

spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);

IWeakEventListener を実装し、その実装での ClockwiseSpin イベントとそのマネージャーを記述するクラスがある場合、弱いイベント パターンを使用する構文は次のようになります。

ClockwiseSpinEventManager.AddListener(spinnerInstance, this);

このイベントの処理ロジックは、クラスでの ReceiveWeakEvent 実装の 1 つのケースにおいて指定されますが、通常のデリゲート ベースのハンドラーとしてではありません。

外部イベントのパターンの実装

弱いイベント パターンの有意義な 1 つの特徴は、コード ベースの一部でないイベントに対してパターンを実装できることです。 ソースの観点からは、ハンドラーがそのイベントにアタッチされる方法には違いがなく、WeakEventManager によって制御されます。 イベントの WeakEventManager を定義し、次に、弱いイベント パターンを使用してイベントをリッスンすると考えられるリスナーの ReceiveWeakEvent ロジックの一部としてイベントを記述する必要があるだけです。

参照

参照

WeakEventManager

IWeakEventListener

概念

ルーティング イベントの概要

データ バインディングの概要