WeakEventManager弱事件模式

小貓會飛發表於2017-06-09

在應用程式中,為了與將處理程式附加到事件源的偵聽器物件配合,附加到該事件源的處理程式可能不會被破壞。這種情況可能會導致記憶體洩漏。 Windows Presentation Foundation (WPF) 引入了可用於解決此問題的設計模式,該模式為特定事件提供專用管理器類併為該事件的偵聽器實現介面。 此設計模式稱為“弱事件模式”。


為什麼實現弱事件模式?


偵聽事件可能會導致記憶體洩漏。 偵聽事件的一般方法是使用語言特定的語法,該語法將處理程式附加到源上的事件。 例如,在 C# 中,該語法是:source.SomeEvent += new SomeEventHandler(MyEventHandler)。

此方法建立從事件源到事件偵聽器的強引用。 通常,為偵聽器附加事件處理程式會導致偵聽器具有物件生存期,該生存期受源的物件生存期影響(除非顯式移除了事件處理程式)。 但在某些情況下,您可能希望偵聽器的物件生存期受其他因素(例如物件生存期當前是否屬於應用程式的視覺化樹)控制,而不受源的生存期控制。 如果源物件生存期超出了偵聽器的物件生存期,則常規事件模式會導致記憶體洩漏:偵聽器保持活動狀態的時間比預期要長。

弱事件模式旨在解決此記憶體洩漏問題。 每當偵聽器需要註冊事件,而該偵聽器並不明確瞭解什麼時候登出時,就可以使用弱事件模式。 當源的物件生存期超出偵聽器的有用物件生存期時,也可以使用弱事件模式。 (在這種情況下,有用由您確定。)使用弱事件模式,偵聽器可以註冊和接收事件,同時不會對偵聽器的物件生存期特徵產生任何影響。 實際上,來自源的隱含引用無法確定偵聽器是否適合進行垃圾回收。 該引用為弱引用,僅是弱事件模式和相關 API 的命名。 可以對偵聽器進行垃圾回收或銷燬,在不保留對現在銷燬的物件的非可收集處理程式引用的情況下,源可以繼續。

誰應實現弱事件模式?


實現弱事件模式主要是對於控制元件作者而言是很有趣味的。 這是因為控制元件作者主要負責控制元件的行為和包容以及控制元件對其所插入到的應用程式的影響。 這包括控制元件物件生存期行為,特別是對所描述的記憶體洩漏問題的處理。

某些方案本質上適合應用弱事件模式。 資料繫結就是這樣一個方案。 在資料繫結中,通常源物件完全獨立於偵聽器物件,而該偵聽器物件是繫結的目標。WPF 資料繫結的許多方面在實現事件的方式上已應用了弱事件模式。

如何實現弱事件模式


可通過三種方式實現弱事件模式。 ,當您應使用每一時,下表列出了三種方法並提供某些指南。

1使用現有弱事件管理器類

如果希望訂閱的事件具有相應的 WeakEventManager,使用現有的弱事件管理器。 有關所附帶的 WPF 弱事件管理器的列表,請參見。WeakEventManager 類的繼承層次結構。 但是,請注意,具有相對包含 WPF 的較弱事件管理器,因此,您可能需要選擇其他方法之一。

2使用泛型弱事件管理器類

使用泛型 WeakEventManager<TEventSource, TEventArgs> ,當現有 WeakEventManager 不可用時,您需要一種簡單的方法實現,因此,您不考慮效能。 泛型 WeakEventManager<TEventSource, TEventArgs> 比現有或自定義弱事件管理器效率低。 例如,泛型類執行更多反射發現命名該事件的名稱。 此外,註冊的程式碼使用常規 WeakEventManager<TEventSource, TEventArgs> 事件使用現有或自定義 WeakEventManager詳細的速度。

3建立自定義弱事件管理器類

建立自定義 WeakEventManager ,在現有 WeakEventManager 不可用時,並且您希望在最好的效能。 使用自定義 WeakEventManager 訂閱事件將更為有效的,但是,則會先編寫更多程式碼的成本。

以下各節描述如何實現弱事件模式。 在本討論中的目的,訂閱的事件具有下列特徵。

  • 操作名稱為 SomeEvent。

  • 事件由 EventSource 類引發。

  • 事件處理程式具有型別: SomeEventEventHandler (或 EventHandler<SomeEventEventArgs>)。

  • 事件通過型別 SomeEventEventArgs 的引數為事件處理程式。


 

使用現有弱事件管理器類

  1. 查詢現有弱事件管理器。

    有關所附帶的 WPF 弱事件管理器的列表,請參見。 WeakEventManager 類的繼承層次結構。

  2. 使用新的弱事件管理器而不是常規事件掛接。

    例如,因此,如果程式碼使用以下模式訂閱事件:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    將其更改為以下模式:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    同樣,因此,如果程式碼使用以下模式取消訂閱事件:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);
    

    將其更改為以下模式:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);
    
使用泛型弱事件管理器類
  1. 使用泛型 WeakEventManager<TEventSource, TEventArgs> 類而不是常規事件掛接。

    當您使用 WeakEventManager<TEventSource, TEventArgs> 註冊事件偵聽器時,如下面的程式碼所示,可以提供事件源和 EventArgs 型別為型別引數提供類並呼叫 AddHandler :

    WeakEventManager<EventSource, SomeEventEventArgs>.AddHandler(source, "SomeEvent", source_SomeEvent);
    
建立自定義弱事件管理器類
  1. 複製下面的類别範本新增到專案。

    此類從 WeakEventManager 類繼承。

    C#

    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 名稱擁有名稱。

  3. 用相應的名稱替換前面所述的三個名稱事件。 (SomeEvent、 EventSource和 SomeEventEventArgs)

  4. 設定可見性 (公共/內部/私有) 弱事件管理器類將與事件它管理的可見性。

  5. 使用新的弱事件管理器而不是常規事件掛接。

    例如,因此,如果程式碼使用以下模式訂閱事件:

    source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
    

    將其更改為以下模式:

    SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
    

    同樣,因此,如果程式碼使用以下模式取消訂閱事件:

    source.SomeEvent -= new SomeEventEventHandler(OnSome);
    

    將其更改為以下模式:

    SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);

相關文章