亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

詳解C#編程中.NET的弱事件模式

2019-10-29 21:41:14
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了C#編程中.NET的弱事件模式,深入討論了C#中相關的垃圾回收機制,需要的朋友可以參考下

引言

你可能知道,事件處理是內存泄漏的一個常見來源,它由不再使用的對象存留產生,你也許認為它們應該已經被回收了,但不是,并有充分的理由。

在這個短文中(期望如此),我會在 .Net 框架的上下文事件處理中展示這個問題,之后我會教你這個問題的標準解決方案,弱事件模式。有兩種方法,即:

“傳統”方法 (嗯,在 .Net 4.5 前,所以也沒那么老),它實現起來比較繁瑣

.Net 4.5 框架提供的新方法,它則是盡其可能的簡單

(源代碼在這里可供使用。)

從常見事物開始

在一頭扎進本文核心內容前,讓我們回顧一下在代碼中最常使用的兩個事物:類和方法。

事件源

讓我為您介紹一個基本但很有用的事件源類,它最低限度地揭示了足夠的復雜性來說明這一點:

 

 
  1. public class EventSource 
  2. public event EventHandlerEvent = delegate { }; 
  3.  
  4. public void Raise() 
  5. Event(this, EventArgs.Empty); 

對好奇那個奇怪的空委托初始化方法(delegate { })的人來說,這是一個用來確保事件總被初始化的技巧,這樣就可以不必每次在使用它之前都要檢查它是否不為NULL。

觸發垃圾收集的實用方法

在.net中,垃圾收集以一種不確定的方式觸發。這對我們的實驗很不利,我們的實驗需要以一種確定的方式跟蹤對象的狀態。

所以,我們必須定期觸發自己的垃圾收集操作,同時避免復制管道代碼,管道代碼已經在在一個特定的方法中釋放:

 

 
  1. static void TriggerGC() 
  2. Console.WriteLine("Starting GC."); 
  3.  
  4. GC.Collect(); 
  5. GC.WaitForPendingFinalizers(); 
  6. GC.Collect(); 
  7.  
  8. Console.WriteLine("GC finished."); 

雖然不是很復雜,但是如果你不是很熟悉這種模式,還是有必要小小解釋一下:

第一個 GC.Collect() 觸發.net的CLR垃圾收集器,對于負責清理不再使用的對象,和那些類中沒有終結器(即c#中的析構函數)的對象,CLR垃圾收集器足夠勝任

GC.WaitForPendingFinalizers() 等待其他對象的終結器執行;我們需要這樣做,因為,你將看到我們使用終結器方法去追蹤我們的對象在什么時候被收集的

第二個GC.Collect() 確保新生成的對象也被清理了

引入問題

首先讓我們試著通過一些理論,最重要的是還有一個演示的幫助,去了解事件監聽器有哪些問題。

背景

一個對象要想被作為事件偵聽器,需要將其實例方法之一登記為另一個能夠產生事件的對象(即事件源)的事件處理程序,事件源必須保持一個到事件偵聽器對象的引用,以便在事件發生時調用此偵聽器的處理方法。

這很合理,但如果這個引用是一個 強引用,則偵聽器會作為事件源的一個依賴 從而不能作為垃圾回收,即使引用它的最后一個對象是事件源。

下面詳細圖解在這下面發生了什么:

事件處理問題

這將不是一個問題,如果你可以控制listener object的生命周期,你可以取消對事件源的訂閱當當你不再需要listener,常常可以使用disposable pattern(用后就扔的模式)。

但是如果你不能在listener生命周期內驗證單點響應,在確定性的方式中你不能把它處理掉,你必須依賴GC處理...這將從不會考慮你所準備的對象,只要事件源還存在著!

例子

理論都是好的,但還是讓我們看看問題和真正的代碼。

這是我們勇敢的時間監聽器,還有點幼稚,我們很快知道為什么:

 

 
  1. public class NaiveEventListener 
  2. private void OnEvent(object source, EventArgs args) 
  3. Console.WriteLine("EventListener received event."); 
  4.  
  5. public NaiveEventListener(EventSource source) 
  6. source.Event += OnEvent; 
  7.  
  8. ~NaiveEventListener() 
  9. Console.WriteLine("NaiveEventListener finalized."); 

用一個簡單例子來看看怎么實現運作:

 

 
  1. Console.WriteLine("=== Naive listener (bad) ==="); 
  2.  
  3. EventSource source = new EventSource(); 
  4.  
  5. NaiveEventListener listener = new NaiveEventListener(source); 
  6.  
  7. source.Raise(); 
  8.  
  9. Console.WriteLine("Setting listener to null."); 
  10. listener = null
  11.  
  12. TriggerGC(); 
  13.  
  14. source.Raise(); 
  15.  
  16. Console.WriteLine("Setting source to null."); 
  17. source = null
  18.  
  19. TriggerGC(); 

輸出:

 

 
  1. EventListener received event. 
  2. Setting listener to null
  3. Starting GC. 
  4. GC finished. 
  5. EventListener received event. 
  6. Setting source to null
  7. Starting GC. 
  8. NaiveEventListener finalized. 
  9. GC finished. 

讓我們分析下這個運作流程:

“EventListener received event.“:這是我們調用 “source.Raise()”的結果; perfect, seems like we're listening.

“Setting listener to null.“: 我們把本地事件監聽器對象引用賦空值,這樣應該可以讓垃圾回收器回收了.

“Starting GC.“: 垃圾回收開始.

“GC finished.“: 垃圾回收開始, 但是 但是我們的事件監聽器沒有被回收器回收, 這樣就證明了事件監聽器的析構函數沒有被調用。

“EventListener received event.“: 第二次調用 “source.Raise()”來確認,發現這監聽器還活著。

“Setting source to null.“: 我們在賦空值給事件的原對象.

“Starting GC.“: 第二次垃圾回收.

“NaiveEventListener finalized.“: 這一次幼稚的事件監聽終于被回收了,遲到總好過沒有.

“GC finished.“:第二次垃圾回收完成.

結論:確實有一個隱藏的對事件監聽器的強引用,目的是防止它在事件源被回收之前被回收!

希望有針對此問題的標準解決方案:讓事件源可以通過弱引用來引用偵聽器,在事件源存在時也可以回收偵聽器對象。

這里有一個標準的模式及其在.NET框架上的實現:弱事件模式(http://msdn.microsoft.com/en-us/library/aa970850.aspx)。 And there is a standard pattern and its implementation in the .Net framework: the weak event pattern.

弱事件模式

讓我們看看在.NET中如何應付這個問題,

通常有超過一種方法去做,但是在這種情況下可以直接決定:

如果你正在使用 .Net 4.5 ,那么你將從簡單的實現受益

另外,你必須依靠一點人為的技巧手段

傳統方式

WeakEventManager 是所有模式管道的封裝

IWeakEventListener 是管道,它允許一個組件連接到WeakEventManager管件

(這兩個位于WindowBase程序集,你將需要參考你自己的如果你不在開發WPF項目,你應該準確的參考WindowBase)

因此這有兩步處理.

首先通過繼承WeakEventManager來實現一個自定義事件管理器:

重寫 StartListening 和 StopListening 方法,分別注冊一個新的handler和注銷一個已存在的; 它們將被WeakEventManager基類使用。

提供兩個方法來訪問listener列表, 命名為 “AddListener” 和 “RemoveListener “,給自定義事件管理器的使用者使用。

通過在自定義事件管理器上暴露一個靜態屬性,提供一個方式去獲得當前線程的事件管理器。

之后使listenr實現IWeakEventListenr接口:

實現 ReceiveWeakEvent 方法

嘗試去處理這個事件

如果無誤的處理好事件,將返回true

有很多要說的,但是可以相對地轉換成一些代碼:

首先是自定義弱事件管理器:

 

 
  1. public class EventManager : WeakEventManager 
  2. private static EventManager CurrentManager 
  3. get 
  4. EventManager manager = (EventManager)GetCurrentManager(typeof(EventManager)); 
  5.  
  6. if (manager == null
  7. manager = new EventManager(); 
  8. SetCurrentManager(typeof(EventManager), manager); 
  9.  
  10. return manager; 
  11.  
  12.  
  13. public static void AddListener(EventSource source, IWeakEventListener listener) 
  14. CurrentManager.ProtectedAddListener(source, listener); 
  15.  
  16. public static void RemoveListener(EventSource source, IWeakEventListener listener) 
  17. CurrentManager.ProtectedRemoveListener(source, listener); 
  18.  
  19. protected override void StartListening(object source) 
  20. ((EventSource)source).Event += DeliverEvent; 
  21.  
  22. protected override void StopListening(object source) 
  23. ((EventSource)source).Event -= DeliverEvent; 

之后是事件listener:

 

 
  1. public class LegacyWeakEventListener : IWeakEventListener 
  2. private void OnEvent(object source, EventArgs args) 
  3. Console.WriteLine("LegacyWeakEventListener received event."); 
  4.  
  5. public LegacyWeakEventListener(EventSource source) 
  6. EventManager.AddListener(source, this); 
  7.  
  8. public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) 
  9. OnEvent(sender, e); 
  10.  
  11. return true
  12.  
  13. ~LegacyWeakEventListener() 
  14. Console.WriteLine("LegacyWeakEventListener finalized."); 

檢查下:

 

  1. Console.WriteLine("=== Legacy weak listener (better) ==="); 
  2.  
  3. EventSource source = new EventSource(); 
  4.  
  5. LegacyWeakEventListener listener = new LegacyWeakEventListener(source); 
  6.  
  7. source.Raise(); 
  8.  
  9. Console.WriteLine("Setting listener to null."); 
  10. listener = null
  11.  
  12. TriggerGC(); 
  13.  
  14. source.Raise(); 
  15.  
  16. Console.WriteLine("Setting source to null."); 
  17. source = null
  18.  
  19. TriggerGC(); 

輸出:

 

 
  1. LegacyWeakEventListener received event. 
  2. Setting listener to null
  3. Starting GC. 
  4. LegacyWeakEventListener finalized. 
  5. GC finished. 
  6. Setting source to null
  7. Starting GC. 
  8. GC finished. 

非常好,它起作用了,我們的事件listener對象現在可以在第一次GC里正確的析構,即使事件源對象還存活,不再泄露內存了.

但是要寫一堆代碼就為了一個簡單的listener,想象一下你有一堆這樣的listener,你必須要為每個類型的寫一個弱事件管理器!

如果你很擅長代碼重構,你可以發現一個聰明的方式去重構所有通用的代碼.

在.Net 4.5 出現之前,你必須自己實現弱事件管理器,但是現在,.Net提供一個標準的解決方案來解決這個問題了,現在就來回顧下吧!

.Net 4.5 方式

.Net 4.5 已介紹了一個新的泛型版本的遺留WeakEventManager: WeakEventManager.

(這個類可以在WindowsBase集合.)

多虧了 .Net WeakEventManager 自己處理泛型, 不用去一個個實現新事件管理器.

而且代碼還簡單和可讀:

 

 
  1. public class WeakEventListener 
  2. private void OnEvent(object source, EventArgs args) 
  3. Console.WriteLine("WeakEventListener received event."); 
  4.  
  5. public WeakEventListener(EventSource source) 
  6. WeakEventManager.AddHandler(source, "Event", OnEvent); 
  7.  
  8. ~WeakEventListener() 
  9. Console.WriteLine("WeakEventListener finalized."); 

簡單的一行代碼,真簡潔.

其他實現的使用也是相似的, 就是裝入所有東西到事件listener類里:

 

 
  1. Console.WriteLine("=== .Net 4.5 weak listener (best) ==="); 
  2.  
  3. EventSource source = new EventSource(); 
  4.  
  5. WeakEventListener listener = new WeakEventListener(source); 
  6.  
  7. source.Raise(); 
  8.  
  9. Console.WriteLine("Setting listener to null."); 
  10. listener = null
  11.  
  12. TriggerGC(); 
  13.  
  14. source.Raise(); 
  15.  
  16. Console.WriteLine("Setting source to null."); 
  17. source = null
  18.  
  19. TriggerGC(); 

輸出也是肯定正確的:

 

 
  1. WeakEventListener received event. 
  2. Setting listener to null
  3. Starting GC. 
  4. WeakEventListener finalized. 
  5. GC finished. 
  6. Setting source to null
  7. Starting GC. 
  8. GC finished. 

預期結果也跟之前一樣,還有什么問題?!

結論

正如你看到的,在.Net上實現弱事件模式 是十分直接, 特別在 .Net 4.5.

如果你沒有用.Net 4.5來實現,將需要一堆代碼, 你可能不去用任何模式而是直接使用C# (+= and -=), 看看是否有內存問題,如果注意到泄露,還需要花必要的時間去實現一個。

但是用 .Net 4.5, 它是自由和簡潔,而且由框架管理, 你可以毫無顧慮的選擇它, 盡管沒有 C# 語法 “+=” 和 “-=” 的酷, 但是語義是清晰的,這才是最重要的.

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品极品尤物在线观看| 欧美极品欧美精品欧美视频| 一区二区三区国产在线观看| 国产一区二区香蕉| 国产成人avxxxxx在线看| 国产精品久久久久一区二区| 久久综合色88| 国产精品夜间视频香蕉| 亚洲va码欧洲m码| 亚洲国产精品va| 热久久视久久精品18亚洲精品| 国产日韩精品在线| 粉嫩老牛aⅴ一区二区三区| 日韩高清免费观看| 亚洲精品大尺度| 日韩精品小视频| 国产精品99导航| 三级精品视频久久久久| 538国产精品一区二区在线| 91中文字幕在线观看| 色综合视频一区中文字幕| 亚州精品天堂中文字幕| 精品成人国产在线观看男人呻吟| 国产精品欧美风情| 欧美成人精品不卡视频在线观看| 日韩电影免费观看在线观看| 日韩电影中文字幕| 亚洲bt欧美bt日本bt| 日韩在线免费av| 日韩中文字幕在线精品| 91色在线观看| 国产日韩中文字幕在线| 38少妇精品导航| 国产成人拍精品视频午夜网站| 欧美成人三级视频网站| 亚洲一区亚洲二区亚洲三区| 91精品国产高清久久久久久久久| www.久久撸.com| 欧美日韩中文字幕日韩欧美| 中文字幕日韩高清| 亚洲激情在线观看| 亚洲国产中文字幕久久网| 亚洲欧洲在线观看| 亚洲黄色在线看| 国产精品福利在线| 国产美女精品免费电影| 久久免费精品视频| 欧美成人免费网| 欧美疯狂做受xxxx高潮| 久久综合久久八八| 欧美亚洲视频一区二区| 国外成人在线直播| 日本一本a高清免费不卡| 国产欧美欧洲在线观看| 亚洲精品久久久久久久久久久| 国产精品视频地址| 国产成人午夜视频网址| 深夜精品寂寞黄网站在线观看| 亚洲欧洲xxxx| 91美女高潮出水| 久久久噜噜噜久噜久久| 91国语精品自产拍在线观看性色| 亚洲图片欧美午夜| 亚洲国产欧美一区二区三区同亚洲| 九九热视频这里只有精品| 亚洲成人av中文字幕| 日韩在线欧美在线国产在线| 亚洲国产精品专区久久| 欧美第一黄色网| 国产成+人+综合+亚洲欧洲| 91色琪琪电影亚洲精品久久| 亚洲欧洲日韩国产| 成人高清视频观看www| 国产日韩欧美视频在线| 亚洲香蕉av在线一区二区三区| www.国产一区| 日本欧美一级片| 亚洲国产精品悠悠久久琪琪| 亚洲成人性视频| 91精品久久久久久久久中文字幕| 91精品视频在线| 91精品久久久久久久久不口人| 国产一区二中文字幕在线看| 欧美成人国产va精品日本一级| 美女啪啪无遮挡免费久久网站| 国产精品成人国产乱一区| 日韩欧美亚洲一二三区| 国外成人在线播放| 亚洲乱码国产乱码精品精| 国产精品久久在线观看| 夜夜嗨av色一区二区不卡| 久久久国产成人精品| 91精品国产91久久久久久久久| 26uuu另类亚洲欧美日本一| 97在线观看免费高清| 欧美片一区二区三区| 国产精品免费视频久久久| 日韩中文在线中文网在线观看| 91热福利电影| 亚洲免费视频观看| 国内精品小视频在线观看| 久久久久久久亚洲精品| 亚洲精品欧美一区二区三区| 欧美天天综合色影久久精品| 成人午夜两性视频| 国产精品视频网址| 91欧美精品成人综合在线观看| 国产一区二区三区日韩欧美| 欧美精品激情视频| 97不卡在线视频| 日韩在线不卡视频| 性色av一区二区三区| 色无极影院亚洲| 久久久久久久久国产精品| 免费97视频在线精品国自产拍| 美女扒开尿口让男人操亚洲视频网站| 国产精品视频999| 在线播放国产精品| 日韩av在线免费观看一区| 国产欧美日韩综合精品| 成人xxxxx| 久久久久久69| 欧美与黑人午夜性猛交久久久| 丝袜亚洲另类欧美重口| 成人精品视频久久久久| 久久精品国产亚洲一区二区| 日韩亚洲精品电影| 久久久久免费精品国产| 中文字幕免费国产精品| 亚洲老司机av| 成人午夜激情免费视频| 久久激情视频免费观看| 欧美午夜激情视频| 97久久精品在线| 国产中文字幕亚洲| 2018日韩中文字幕| 欧美大全免费观看电视剧大泉洋| 亚洲人成77777在线观看网| 亚洲男女自偷自拍图片另类| 久久久91精品国产一区不卡| 欧美成人高清视频| 精品国产网站地址| 97超碰蝌蚪网人人做人人爽| 久久久久久香蕉网| 成人午夜激情免费视频| 国产视频久久久久久久| 一区二区国产精品视频| 亚洲人成电影网站色www| 国产精品久久久久久久一区探花| 国产亚洲视频中文字幕视频| 欧美日韩xxxxx| 国产美女主播一区| 一级做a爰片久久毛片美女图片| www.亚洲天堂| 中文字幕亚洲一区二区三区| 欧美午夜片在线免费观看| 57pao精品| 欧美视频在线观看 亚洲欧| 亚洲国产另类久久精品| 亚洲日韩欧美视频| 国产噜噜噜噜噜久久久久久久久| 欧美色道久久88综合亚洲精品| 亚洲伊人一本大道中文字幕| 91精品国产91久久久久久吃药|