2010-04-23 103 views
43

在我的.NET應用程序中,我正在訂閱來自另一個類的事件。訂閱是有條件的。我在控件可見時訂閱事件,並在它變得不可見時取消訂閱。但是,在某些情況下,即使控件不可見,我也不想取消訂閱事件,因爲我想要在後臺線程上發生的操作的結果。如何確定事件是否已訂閱

有沒有一種方法可以確定一個類是否已經訂閱了該事件?

我知道我們可以在課堂上通過檢查null的事件來提高該事件,但我該如何在訂閱該事件的課程中做到這一點?

+2

檢查此鏈接http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/d7d8791f-6aef-4fda-ae0e-5eddcb856706/ – 2010-04-23 09:00:04

+0

如果只有閹* *人訂閱:'bool subscribedTo = theEvent!= null' – Mark 2014-07-23 14:24:31

回答

51

event關鍵字明確地發明,以防止你做你想做的事情。它限制對底層對象delegate的訪問,所以沒有人可以直接混淆它存儲的事件處理程序訂閱。活動是訪問者代表,就像屬性是一個字段的訪問者。一個屬性只允許獲取和設置,一個事件只允許添加和刪除。

這使您的代碼安全,其他代碼只能在知道事件處理程序方法和目標對象時移除事件處理程序。 C#語言通過不允許您命名目標對象來提供額外的安全層。

並且WinForms提供了額外的安全層,因此即使使用Reflection也會變得很困難。它將delegate實例存儲在一個EventHandlerList中,祕密「cookie」作爲密鑰,您必須知道該cookie才能將對象從列表中挖出。

那麼,不要去那裏。這是小事與你結束一段代碼來解決問題:

private bool mSubscribed; 

private void Subscribe(bool enabled) 
{ 
    if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged; 
    else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged; 

    mSubscribed = enabled; 
} 
+4

您是否碰巧有任何博客,頻道9視頻或MSDN文章討論使* events *很難與之交互的背後的設計方法?也許如果我理解了*爲什麼*,以及什麼*意圖*機制來完成一些(通常)微不足道的事情,我可能會有更容易的時間爲我自己的問題集提供簡單的解決方案。 – 2011-11-17 23:38:18

+1

您的活動並不難與之互動。與其他人的事件混淆是困難的,這是與私人部分混淆。詢問一個關於它的問題。 – 2011-11-18 00:18:37

+2

好的,*爲什麼*要搞亂別人的事件? – 2011-11-18 02:13:08

0

難道你不能只記得你是否已經訂閱?迄今爲止,這種方法對我來說效果不錯。即使你有很多事件或對象,你仍然可能只想記住它(例如在一本字典中)。

另一方面,可見性的變化至少對我來說不是一個好的訂閱/取消訂閱點。我通常寧願使用建築/處置,這比每次可見性變化都更清晰。

6

假設您無權訪問聲明該事件的類的內部,您無法直接執行該操作。事件只會暴露運營商+=-=,沒有別的。你需要在你的訂閱類中有一個標誌或其他機制來知道你是否已經訂閱了。

1

您可以將決策邏輯放入觸發事件的方法中嗎?假設你正在使用的WinForms它會是這個樣子:

if (MyEvent != null && isCriteriaFulfilled) 
{ 
    MyEvent(); 
} 

isCriteriaFulfilled是由您的顯示/隱藏邏輯決定的。

//最新通報/////

而且你的第一個評論會是沒有意義的改變行爲的事件處理中視this.Visible價值?

a.Delegate += new Delegate(method1); 
... 
private void method1() 
{ 
    if (this.Visible) 
     // Do Stuff 
} 

或者,如果你真的有去註冊和取消:

private Delegate _method1 = null; 
... 
if(this.visible) 
{ 
    if (_method1 == null) 
     _method1 = new Delegate(method1); 
    a.Delegate += _method1; 
} 
else if (_method1 != null) 
{ 
    a.Delegate -= _method1; 
} 
+1

我不知道'areCriteriaFulfilled'在語法上更好嗎? – 2010-04-23 09:13:16

+0

我正在做如下操作 if(this.visible) a.Delegate + = new Delegate(method1); } else { a.Delegate - = new Delegate(method1); } – Ram 2010-04-23 09:51:07

+0

@Ram:更新了答案。 – 2010-04-23 10:19:54

1

簡單的檢查控制是否是可見或不可見每當觸發事件處理程序。

+0

我不想這樣做,因爲事件是以固定的時間間隔觸發的,我只想在我的控件不可見時才使用它們。如果我按照你所說的去做,那將會是一場表演。 – Ram 2010-04-23 10:09:16

+0

@Ram:你爲什麼認爲這將會是一場表演?你衡量了性能的變化嗎? – 2010-04-23 10:38:21

+0

@菲爾:嗨菲爾,這是一個性能問題,因爲我正在用多種形式和多個事件進行此操作。每個表單處理數據差異的方式。所以爲了避免處理數據,我訂閱的事件只有形式是可見的。 我相信使用布爾值將是一個不錯的選擇。 – Ram 2010-04-23 10:51:11

2
/// <summary> 
    /// Determine if a control has the event visible subscribed to 
    /// </summary> 
    /// <param name="controlObject">The control to look for the VisibleChanged event</param> 
    /// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns> 
    private bool IsSubscribed(Control controlObject) 
    { 
    FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible", 
     BindingFlags.Static | BindingFlags.NonPublic); 
    object object_value = event_visible_field_info.GetValue(controlObject); 
    PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events", 
     BindingFlags.NonPublic | BindingFlags.Instance); 
    EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null); 
    return (event_list[object_value] != null); 
    } 
0

我只是在漢斯的答案擴大。我只是想確保我不會多次安裝我的處理程序,並且在我仍然需要時不會刪除它。這並不能防止惡意或惡意呼叫者反覆取消訂閱,因爲您需要跟蹤呼叫者,而這隻會讓您反覆重複訂閱而超出跟蹤機制。

// Tracks how many times the ReflectionOnlyResolveHandler has been requested. 
private static int _subscribers = 0; 

/// <summary> 
/// Register or unregister the ReflectionOnlyResolveHandler. 
/// </summary> 
/// <param name="enable"></param> 
public static void SubscribeReflectionOnlyResolve(bool enable) 
{ 
    lock(_lock) 
    { 
     if (_subscribers > 0 && !enable) _subscribers -= 1; 
     else if (enable) _subscribers += 1; 

     if (enable && _subscribers == 1) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler; 
     else if (_subscribers == 0) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler; 
    } 
} 
+0

上面的調用者並不是真正的「訂閱者」,他們只是要求幫助者類註冊它的處理程序。 – 2016-07-16 09:23:47