2010-11-13 78 views
27

我有3個問題,關於事件:我應該取消訂閱活動嗎?

  1. 我就應該總是退訂事件進行訂閱?
  2. 如果我沒有,會發生什麼?
  3. 在以下示例中,您將如何取消訂閱訂閱事件?

我有例如下面的代碼:

構造函數:用途:用於數據庫屬性更新

this.PropertyChanged += (o, e) => 
{ 
    switch (e.PropertyName) 
    { 
     case "FirstName": break; 
     case "LastName": break; 
    } 
}; 

這:用途:用於GUI結合包住模式進入的ViewModels

ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate); 
PeriodListViewModel = new ObservableCollection<PeriodViewModel>(); 

foreach (Period period in periods) 
{ 
    PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo); 
    foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList) 
    { 
     documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument); 
     documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument); 
     documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument); 
    } 
    PeriodListViewModel.Add(periodViewModel); 
} 
+0

http:// stackoverflow。com/questions/1061727/is-it-bad-to-not-unregister-event-handlinglers – SwDevMan81 2010-11-13 19:00:01

回答

23

1)這取決於。通常這是一個好主意,但有一些典型情況你不需要。基本上,如果您確定訂閱對象將超過事件源,則應該取消訂閱,否則這會造成不必要的參考。

如果您但是對象訂閱它自己的事件,如以下:

<Window Loaded="self_Loaded" ...>...</Window> 

--then你不必。

2)訂閱一個事件會對訂閱對象產生額外的引用。所以如果你不取消訂閱,你的對象可能會被這個引用保持活着,從而有效地發生內存泄漏。通過取消訂閱,您將刪除該參考。請注意,在自訂的情況下,問題不會出現。

3)你可以做這樣的:

​​

其中

void PropertyChangedHandler(object o, PropertyChangedEventArgs e) 
{ 
    switch (e.PropertyName) 
    { 
     case "FirstName": break; 
     case "LastName": break; 
    } 
} 
+0

當我使用你的底層代碼時我無法編譯?我把一個「;」在最後一個「}」的後面,但它沒有編譯?我應該在哪裏取消訂閱您的建議代碼? – Elisabeth 2010-11-13 17:49:29

+0

@Lisa:(1)你得到的錯誤信息是什麼? (2)當你不再需要這些事件時,你應該取消訂閱。通常這是對象生命週期的結束。如果您可以更多地瞭解要訂閱事件的對象,則可以更輕鬆地瞭解何時取消訂閱。 – Vlad 2010-11-13 18:02:26

+0

查看我上面的代碼我訂閱了3個事件OnXXX方法。 PeriodViewModel不能同時刪除DocumentListViewModel。 – Elisabeth 2010-11-13 20:10:22

4

您可以在MSDN上查看this article。報價:

要防止事件處理程序從 當事件是 提出被調用,只需從 事件退訂。爲了防止資源泄漏,在處理用戶對象之前,從事件中取消訂閱 是很重要的。直到你 從事件退訂,構成了這一 事件發佈對象的 多播委託具有 參考代表的是 封裝了用戶的事件 處理。只要發佈 對象擁有該引用,您的 訂戶對象將不會收集垃圾 。

1

1)如果我總是desubscribe事件訂閱呢?
通常是的。唯一的例外是當你訂閱的對象不再被引用,並且很快就會被垃圾回收。

2.)如果我不這樣做會怎麼樣?
您訂閱的對象將持有對委託的引用,委託又引用其指針this,因此您將收到內存泄漏。
或者如果處理程序是一個lamda,它將保存它綁定的任何局部變量,因此它也不會被收集。

+1

我會指出,如果您不退訂該事件,那麼您的處理程序可以在完成使用該對象後調用;如果物體是一次性的,那麼在物體被處理後。如果它使用由dispose釋放的非託管資源,這可能會對處理程序代碼產生影響。 – 2010-11-13 14:02:28

39

好吧,我們先來談談最後一個問題。您不能可靠地取消訂閱您直接使用lambda表達式訂閱的事件。你要麼需要保持一個變量與代表在(所以你仍然可以使用lambda表達式)你需要使用方法組轉換。

現在至於你是否真的需要取消訂閱,它取決於事件生產者和事件消費者之間的關係。如果活動生產者應該活動的時間超過活動消費者的時間,那麼您應該取消訂閱 - 因爲否則生產者會提及消費者,使其活動的時間超過應有的時間。只要生產者生產它,事件處理程序也將繼續被調用。

現在在許多情況下這不是問題 - 例如,在一個表單中,引發Click事件的按鈕可能會生存的時間大約與創建它的表單相同,處理器通常是訂閱的。所以沒有必要退訂。這對於GUI非常典型。

同樣,如果僅爲單個異步請求創建WebClient,則訂閱相關事件並啓動異步請求,然後在請求完成時WebClient本身將有資格進行垃圾回收(假設您不願意在其他地方不保留參考)。

基本上,你應該總是考慮生產者和消費者之間的關係。如果製片人的壽命超過了你想要的消費者,它會在你不再感興趣後繼續提高事件,那麼你應該退訂。

+0

event producer = documentListViewModel?事件消費者=上面的OnXXX方法?你的意思是如果我要刪除一個documentListViewModel還有一個OnXXX方法的引用?我應該在哪裏取消訂閱ViewModel中的事件? – Elisabeth 2010-11-13 16:42:39

2

當訂閱的實例與正在訂閱的實例具有相同的範圍時,您不必從事件中取消訂閱。

可以說你是一個表格,你正在訂閱一個控件,這兩個組成一個組。但是,如果您有一個管理表單的中心班,並且您已訂閱該表單的Closed事件,則這些表不會一起組成一個組,並且您必須在表單關閉後取消訂閱。

訂閱一個事件使訂閱的實例創建一個對正在訂閱的實例的引用。這可以防止垃圾收集。所以,當你有一個管理表單實例的中心類時,這會將所有表單都保存在內存中。

WPF是一個例外,因爲它具有弱事件模型,其中事件訂閱使用弱引用,並且它不會在內存中保存表單。但是,當你不是表格的一部分時,取消訂閱仍然是最佳做法。