弱いイベント パターンWeak Event Patterns

アプリケーションでは、可能であれば、イベント ソースに接続されているハンドラーは破棄されません、ハンドラーをソースに接続されているリスナー オブジェクトと連携します。In applications, it is possible that handlers that are attached to event sources will not be destroyed in coordination with the listener object that attached the handler to the source. このような状況は、メモリ リークが発生する可能性があります。This situation can lead to memory leaks. Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) この問題に対処、特定のイベントの専用マネージャー クラスを提供して、そのイベントのリスナーにインターフェイスを実装して使用できるデザイン パターンについて説明します。 introduces a design pattern that can be used to address this issue, by providing a dedicated manager class for particular events and implementing an interface on listeners for that event. この設計パターンと呼ばれる、弱いイベント パターンです。This design pattern is known as the weak event pattern.

弱いイベント パターンを実装する理由Why Implement the Weak Event Pattern?

イベントのリッスンと、メモリ リークが発生する可能性があります。Listening for events can lead to memory leaks. イベントをリッスンするための一般的な手法では、ソースでのイベントにハンドラーをアタッチする言語固有の構文を使用します。The typical technique for listening to an event is to use the language-specific syntax that attaches a handler to an event on a source. たとえば、C# の場合、その構文は:source.SomeEvent += new SomeEventHandler(MyEventHandler)です。For example, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).

この手法は、強い参照をイベント ソースからのイベント リスナーを作成します。This technique creates a strong reference from the event source to the event listener. オブジェクトの有効期間が影響を受けるソースのオブジェクトの有効期間 (しない限り、イベント ハンドラーが明示的に削除) にリスナーが通常は、リスナーのイベント ハンドラーをアタッチするとします。Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that is influenced by the object lifetime of the source (unless the event handler is explicitly removed). 特定の状況で、ソースの有効期間ではなく、アプリケーションのビジュアル ツリーに現在属しているかどうかなどするその他の要因によって制御されているリスナーのオブジェクトの有効期間をする可能性があります。But in certain circumstances, you might want the object lifetime of the listener to be controlled by other factors, such as whether it currently belongs to the visual tree of the application, and not by the lifetime of the source. ソース オブジェクトの有効期間は、リスナーのオブジェクトの有効期間からはみ出した、ときに通常のイベント パターンは、メモリ リークが発生につながります。 リスナーが有効のまま保持ためのものよりも長い時間です。Whenever the source object lifetime extends beyond the object lifetime of the listener, the normal event pattern leads to a memory leak: the listener is kept alive longer than intended.

弱いイベント パターンは、このメモリ リークの問題を解決するために設計されています。The weak event pattern is designed to solve this memory leak problem. リスナーは、イベントに登録する必要がありますが、リスナーは明示的に認識できない場合の登録を解除するたびに、弱いイベント パターンを使用できます。The weak event pattern can be used whenever a listener needs to register for an event, but the listener does not explicitly know when to unregister. 弱いイベント パターンは、ソースのオブジェクトの有効期間は、リスナーの役に立つオブジェクトの有効期間を超えた場合にも使用できます。The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (この場合、便利するによって決定されます)。弱いイベント パターンは、を登録し、任意の方法でリスナーのオブジェクトの有効期間の特性に影響を与えずに、イベントを受信するリスナーを使用できます。(In this case, useful is determined by you.) The weak event pattern allows the listener to register for and receive the event without affecting the object lifetime characteristics of the listener in any way. 実際には、ソースからの暗黙的な参照では、リスナーは、ガベージ コレクションの条件に適合するかどうかは不明です。In effect, the implied reference from the source does not determine whether the listener is eligible for garbage collection. 参照は、弱いイベント パターンとの関連する名前付けつまり、弱い参照APIAPIsです。The reference is a weak reference, thus the naming of the weak event pattern and the related APIAPIs. リスナーはガベージ コレクトされ、それ以外の場合は破棄、ソースが破棄されたオブジェクトへの収集ハンドラーの参照を保持せずに続行できます。The listener can be garbage collected or otherwise destroyed, and the source can continue without retaining noncollectible handler references to a now destroyed object.

弱いイベント パターンを実装する必要がありますか。Who Should Implement the Weak Event Pattern?

弱いイベント パターンを実装するは、主にコントロールの作成者にとって重要です。Implementing the weak event pattern is interesting primarily for control authors. コントロールの作成者は、ユーザーは、動作や、コントロールと挿入されているアプリケーションへの影響の含有関係担当大きくします。As a control author, you are largely responsible for the behavior and containment of your control and the impact it has on applications in which it is inserted. これが含まれています、コントロール オブジェクトの有効期間の動作では、具体的には記載されているメモリ リークの問題の処理には、します。This includes the control object lifetime behavior, in particular the handling of the described memory leak problem.

特定のシナリオでは、弱いイベント パターンをアプリケーション自体本質的に役立ちます。Certain scenarios inherently lend themselves to the application of the weak event pattern. このようなシナリオの 1 つは、データ バインディングです。One such scenario is data binding. データ バインディングでは、ソース オブジェクトのバインディングのターゲットは、そのリスナー オブジェクトを完全に独立した一般的なです。In data binding, it is common for the source object to be completely independent of the listener object, which is a target of a binding. 多くの側面WPFWPF弱いイベント パターン、イベントの実装方法で適用されるバインディングが既にデータがあります。Many aspects of WPFWPF data binding already have the weak event pattern applied in how the events are implemented.

弱いイベント パターンを実装する方法How to Implement the Weak Event Pattern

弱いイベント パターンを実装する次の 3 つの方法はあります。There are three ways to implement weak event pattern. 次の表は、3 つの方法を示し、それぞれを使用する際のガイダンスを紹介します。The following table lists the three approaches and provides some guidance for when you should use each.

方法Approach 実装する場合When to Implement
既存の弱いイベント マネージャー クラスを使用します。Use an existing weak event manager class サブスクライブするイベントが、対応するWeakEventManager、既存の弱いイベント マネージャーを使用します。If the event you want to subscribe to has a corresponding WeakEventManager, use the existing weak event manager. WPF に含まれている弱いイベント マネージャーの一覧は、継承階層を参照してください、WeakEventManagerクラスです。For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class. ただし、他のアプローチのいずれかを選択する必要がありますので、WPF に含まれる比較的少数の弱いイベント マネージャーがあることに注意してください。Note, however, that there are relatively few weak event managers that are included with WPF, so you will probably need to choose one of the other approaches.
ジェネリックの弱いイベント マネージャー クラスを使用します。Use a generic weak event manager class ジェネリック型を使用してWeakEventManager<TEventSource,TEventArgs>既存WeakEventManagerが利用できないを実装する簡単な方法を目的し、していない関係する効率。Use a generic WeakEventManager<TEventSource,TEventArgs> when an existing WeakEventManager is not available, you want an easy way to implement, and you are not concerned about efficiency. ジェネリックWeakEventManager<TEventSource,TEventArgs>既存またはカスタムの弱いイベント マネージャーより効率が下がります。The generic WeakEventManager<TEventSource,TEventArgs> is less efficient than an existing or custom weak event manager. たとえば、discover イベントのイベントの名前を指定するために複数のリフレクションでは、ジェネリック クラス。For example, the generic class does more reflection to discover the event given the event's name. ジェネリックを使用して、イベントを登録するコードも、WeakEventManager<TEventSource,TEventArgs>がより既存を使用するよりも詳細なまたは customWeakEventManagerです。Also, the code to register the event by using the generic WeakEventManager<TEventSource,TEventArgs> is more verbose than using an existing or custom WeakEventManager.
カスタムの弱いイベント マネージャー クラスを作成します。Create a custom weak event manager class カスタム作成WeakEventManager既存WeakEventManagerことはできませんし、最適な効率。Create a custom WeakEventManager when an existing WeakEventManager is not available and you want the best efficiency. 使用するカスタムWeakEventManagerをサブスクライブするイベントをより効率的になりますが、先頭にさらにコードを記述するコストが発生する操作を行います。Using a custom WeakEventManager to subscribe to an event will be more efficient, but you do incur the cost of writing more code at the beginning.

次のセクションでは、弱いイベント パターンを実装する方法について説明します。The following sections describe how to implement the weak event pattern. この説明の目的は、サブスクライブするイベントは、次の特性を持ちます。For purposes of this discussion, the event to subscribe to has the following characteristics.

  • イベント名がSomeEventです。The event name is SomeEvent.

  • このイベントは、EventSourceクラスです。The event is raised by the EventSource class.

  • イベント ハンドラーが型: SomeEventEventHandler (またはEventHandler<SomeEventEventArgs>)。The event handler has type: SomeEventEventHandler (or EventHandler<SomeEventEventArgs>).

  • このイベントは、型のパラメーターを渡しますSomeEventEventArgsイベント ハンドラーにします。The event passes a parameter of type SomeEventEventArgs to the event handlers.

既存の弱いイベント マネージャー クラスを使用します。Using an Existing Weak Event Manager Class

  1. 既存の弱いイベント マネージャーが見つかりません。Find an existing weak event manager.

    WPF に含まれている弱いイベント マネージャーの一覧は、継承階層を参照してください、WeakEventManagerクラスです。For a list of weak event managers that are included with WPF, see the inheritance hierarchy in the WeakEventManager class.

  2. 通常のイベント フックアップの代わりに新しいの弱いイベント マネージャーを使用します。Use the new weak event manager instead of the normal event hookup.

    たとえば、コードでは、次のパターンを使用してイベントにサブスクライブします。For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);  
    

    次のパターンに変更します。Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);  
    

    同様に、コードがイベントをアンサブスク ライブするのに次のパターンを使用する場合。Similarly, if your code uses the following pattern to unsubscribe to an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);  
    

    次のパターンに変更します。Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);  
    

ジェネリックの弱いイベント マネージャー クラスを使用します。Using the Generic Weak Event Manager Class

  1. ジェネリックを使用してWeakEventManager<TEventSource,TEventArgs>通常のイベント フックアップではなくクラスです。Use the generic WeakEventManager<TEventSource,TEventArgs> class instead of the normal event hookup.

    使用するとWeakEventManager<TEventSource,TEventArgs>イベント リスナーを登録するには、イベント ソースを指定し、EventArgsへの呼び出し、クラス型のパラメーターとして型AddHandler次のコードに示すように。When you use WeakEventManager<TEventSource,TEventArgs> to register event listeners, you supply the event source and EventArgs type as the type parameters to the class and call AddHandler as shown in the following code:

    WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);  
    

カスタムの弱いイベント マネージャー クラスを作成します。Creating a Custom Weak Event Manager Class

  1. 次のクラス テンプレートをプロジェクトにコピーします。Copy the following class template to your project.

    このクラスから継承、WeakEventManagerクラスです。This class inherits from the WeakEventManager class.

    class SomeEventWeakEventManager : WeakEventManager
    {
    
        private SomeEventWeakEventManager()
        {
    
        }
    
        /// <summary>
        /// Add a handler for the given source's event.
        /// </summary>
        public static void AddHandler(EventSource source, 
                                      EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");
    
            CurrentManager.ProtectedAddHandler(source, handler);
        }
    
        /// <summary>
        /// Remove a handler for the given source's event.
        /// </summary>
        public static void RemoveHandler(EventSource source, 
                                         EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");
    
            CurrentManager.ProtectedRemoveHandler(source, handler);
        }
    
        /// <summary>
        /// Get the event manager for the current thread.
        /// </summary>
        private static SomeEventWeakEventManager CurrentManager
        {
            get
            {
                Type managerType = typeof(SomeEventWeakEventManager);
                SomeEventWeakEventManager manager = 
                    (SomeEventWeakEventManager)GetCurrentManager(managerType);
    
                // at first use, create and register a new manager
                if (manager == null)
                {
                    manager = new SomeEventWeakEventManager();
                    SetCurrentManager(managerType, manager);
                }
    
                return manager;
            }
        }
    
    
    
        /// <summary>
        /// Return a new list to hold listeners to the event.
        /// </summary>
        protected override ListenerList NewListenerList()
        {
            return new ListenerList<SomeEventEventArgs>();
        }
    
    
        /// <summary>
        /// Listen to the given source for the event.
        /// </summary>
        protected override void StartListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Stop listening to the given source for the event.
        /// </summary>
        protected override void StopListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }
    
        /// <summary>
        /// Event handler for the SomeEvent event.
        /// </summary>
        void OnSomeEvent(object sender, SomeEventEventArgs e)
        {
            DeliverEvent(sender, e);
        }
    }
    
  2. 置換、SomeEventWeakEventManager名に置き換えて名前。Replace the SomeEventWeakEventManager name with your own name.

  3. イベントの対応する名前の前に説明した 3 つの名前に置き換えます。Replace the three names described previously with the corresponding names for your event. (SomeEventEventSource、およびSomeEventEventArgs)(SomeEvent, EventSource, and SomeEventEventArgs)

  4. 管理イベントと同様に表示するには、弱いイベント マネージャー クラスの可視性 (パブリック/内部/プライベート) を設定します。Set the visibility (public / internal / private) of the weak event manager class to the same visibility as event it manages.

  5. 通常のイベント フックアップの代わりに新しいの弱いイベント マネージャーを使用します。Use the new weak event manager instead of the normal event hookup.

    たとえば、コードでは、次のパターンを使用してイベントにサブスクライブします。For example, if your code uses the following pattern to subscribe to an event:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);  
    

    次のパターンに変更します。Change it to the following pattern:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);  
    

    同様に、コードがイベントをアンサブスク ライブするのに次のパターンを使用する場合。Similarly, if your code uses the following pattern to unsubscribe to an event:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);  
    

    次のパターンに変更します。Change it to the following pattern:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);  
    

関連項目See Also

WeakEventManager
IWeakEventListener
ルーティング イベントの概要Routed Events Overview
データ バインディングの概要Data Binding Overview