弱式事件模式
在應用程式中,在與將處理常式附加到來源的接聽項物件搭配使用時,很可能不會終結附加到事件來源的處理常式。 這種情況可能會導致記憶體遺漏 (Memory Leak)。 Windows Presentation Foundation (WPF) 引入的設計模式可以用來解決這個問題,方法是藉由提供專屬的管理員類別給特定事件,並在該事件的接聽項上實作介面。 這個設計模式稱為「弱式事件模式」。
為何要實作弱式事件模式
接聽事件可能會導致記憶體遺漏。 一般用於接聽事件的技術,是使用語言特定的語法來附加處理常式到來源事件。 例如,在 C# 中,語法是 source.SomeEvent += new SomeEventHandler(MyEventHandler)。
這個技術會建立事件來源對事件接聽項的強式參考。 一般而言,為接聽項附加事件處理常式會讓接聽項的物件存留期受到來源物件存留期的影響 (除非明確移除事件處理常式)。 但在有些情況下,您會希望接聽項的物件存留期只受到其他因素的控制,例如接聽項目前是否屬於應用程式的視覺化樹狀結構中,而不希望受到來源的存留期控制。 一旦來源物件存留期超過接聽項的物件存留期時,一般的事件模式會導致記憶體遺漏:接聽項存留的時間比預期長。
弱式事件模式就是設計來解決這個記憶體遺漏問題。 弱式事件模式可用於當接聽項需要註冊事件,但未確切了解應在何時取消註冊的時候。 當來源的物件存留期超過接聽項可用的物件存留期時,也能使用弱式事件模式 (在這種情況下,此處的 useful 是由您決定的)。弱式事件模式可以在不以任何方式影響接聽項的物件存留期特性的情況下,允許接聽項註冊和接收事件。 實際上,在決定接聽項是否適合記憶體回收時,並不會判斷來源的隱含參考。 該參考是弱式參考,因而命名為弱式事件模式和相關的 APIs。 接聽項可以經記憶體回收或由其他方式終結,且來源可以繼續,而不需要保留不可回收的處理常式參考給現在已終結的物件。
哪些人應該實作弱式事件模式
會對實作弱式事件模式有興趣的人主要是控制項作者。 身為控制項作者,您主要負責控制項的行為和內含項目,以及控制項對於其插入的應用程式的影響。 這包括控制項物件存留期行為,特別是有關所描述的記憶體遺漏問題的處理。
某些案例本身就提供弱式事件模式的應用。 資料繫結便屬於這類型的案例。 在資料繫結中,來源物件通常和接聽項物件 (繫結的目標) 完全無關。 在 WPF 資料繫結的許多方面中,弱式事件模式已套用在事件的實作方式上。
如何實作弱式事件模式
弱式事件模式的實作可分為以下三個方面:
從 WeakEventManager 類別衍生管理員。
在想要註冊弱式事件接聽項而不要產生來源的強式參考的任何類別上,實作 IWeakEventListener 介面。
註冊接聽項時,對於您想要接聽項使用模式的地方,不要使用事件的普通 add 和 remove 存取子。 請改為針對該事件在專屬的 WeakEventManager 中使用 AddListener 和 RemoveListener 實作。
WeakEventManager
若要實作弱式事件模式,您通常會和事件建立一對一關係的管理員類別。 例如,如果您有名為 Spin 的事件,則會建立 SpinEventManager 類別做為該事件的專屬弱式事件管理員。 如果事件存在於一個以上的類別中,且在每個類別中的行為大致相同,並會共用相同的事件資料型別,則可以將相同的管理員用於每個事件中。
在從 WeakEventManager 類別衍生時,您會覆寫兩個虛擬方法和公開數個其他成員 (成員名稱不是特別由虛擬樣板管理,但仍然存在的)。 覆寫作業是用於讓 WPF 基礎結構啟始或結束事件傳遞模式。 而其他成員則負責提供功能,這樣您自己的 IWeakEventListener 實作可以使用 WeakEventManager 來附加事件的接聽項。
如需衍生自 WeakEventManager 的詳細資訊,請參閱 WeakEventManager 參考主題中的<繼承者注意事項>一節。
IWeakEventListener
IWeakEventListener 介面有一個介面方法,即 ReceiveWeakEvent。 ReceiveWeakEvent 實作必須是集中式實作,會將該類別上存在的任何事件參考導向適當的 WeakEventManager。
如需實作 IWeakEventListener 介面的詳細資訊,請參閱 ReceiveWeakEvent 方法參考主題中的<實作者注意事項>一節。
附加接聽項
假設您的 ClockwiseSpin 事件 (由 Spinner 型別定義) 是普通的事件。 如果您的 SpinListener 接聽項類別要成為接聽項,則用於附加處理常式的普通技術 (不是使用弱式事件模式) 應該使用 += 語法:
spinnerInstance.ClockwiseSpin += new EventHandler(MyOnCWSpinHandler);
如果您的類別會實作 IWeakEventListener,並負責實作中的 ClockwiseSpin 事件和其管理員,則使用弱式事件模式的語法為:
ClockwiseSpinEventManager.AddListener(spinnerInstance, this);
針對該事件的處理邏輯是指定在其中一個類別 ReceiveWeakEvent 實作內,而不是普通的委派處理常式。
實作外部事件的模式
弱式事件模式有趣的一點是,您可以對程式碼基底外的事件實作模式。 從來源的觀點來看,處理常式附加到事件的方式沒有不同,並是由 WeakEventManager 控制的。 您只需要定義該事件的 WeakEventManager,然後在任何預期要使用弱式事件模式來接聽該事件的接聽項上,將該事件視為 ReceiveWeakEvent 邏輯的一部分。