2010-08-02 28 views
0

我努力爲這個問題寫一個更合適的'標題' - 當我更好地理解解決方案和正確的術語時,我會嘗試清理它。 :D如何通過另一個「聚合對象」在一組相同的對象(相同的類 - 不同的實例)上聚合事件?

有一種模式在我一直在研究的最後兩個項目中重複使用(項目都是大量事件驅動的)。

的模式是:

public interface ISourceOfEvents { 
    event Action<EventInfo1> SomethingHappened1; 
    event Action<EventInfo2> SomethingHappened2; 
    {...} 
} 

public class SingleSourceOfEvents : ISourceOfEvents { 
    public event Action<EventInfo1> SomethingHappened1 = delegate { }; 
    public event Action<EventInfo2> SomethingHappened2 = delegate { }; 
    {...} 
} 

public class GroupSourceOfEvents : ISourceOfEvents { 

    public IList<ISourceOfEvents> SourceOfEventsGroup { get; private set; } 

    public event Action<EventInfo1> SomethingHappened1 = delegate { }; 
    public event Action<EventInfo2> SomethingHappened2 = delegate { }; 
    {...} 

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) { 

     SourceOfEventsGroup = sourceOfEventsGroup; 

     foreach (var s in SourceOfEventsGroup) { 

      s.SomethingHappened1 += x => SomethingHappened1(x); 
      s.SomethingHappened2 += x => SomethingHappened2(x); 
      {...} 
     } 
    } 
} 

我的問題歸結爲「我怎麼做出GroupSourceOfEvents構造自動映射的事件綁定?」。

目前代碼很難維護,因爲項目中有多個地方需要添加新事件。 ISourceOfEvents接口允許我將事件添加到中心位置,並且在需要添加事件的任何地方都可以收到編譯器錯誤,但我需要類似的解決方案來處理GroupSourceOfEvents中的事件綁定器映射 - 或者它是完全動態的。

我想類似這樣的東西:

// PSEUDO-CODE WARNING! :D 
// PSEUDO-CODE WARNING! :D 
// PSEUDO-CODE WARNING! :D 

public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) { 

    SourceOfEventsGroup = sourceOfEventsGroup; 

    var events = typeof(ISourceOfEvents).GetEvents(); 

    foreach (var s in SourceOfEventsGroup) { 

     foreach (var e in events) { 
      e.AddEventHandler(s, e.GetRaiseMethod(this)); 
     } 
    } 
} 

可以說,我的問題是參數映射到一起。在當前工作代碼(頂部示例)中使用了lambda表達式,但我不確定如何使用我在我的僞代碼(底部示例)中使用的'樣式'技術來實現這一點。另一半的問題是GetRaiseMethod(this)返回MethodInfo反對delegate(這是我需要使用AddEventHandler),我不知道如何「得到」delegate

任何幫助將不勝感激。我也接受不同的模式或方法,可能更標準(如果這不是)。

我使用C#和.NET 4

回答

1

我不知道如果我完全理解你想要做什麼;根據我的理解,您嘗試創建一個包含ISourceOfEvents對象列表的「聚合器」對象(GroupSourceOfEvents)。

然後,您希望允許訂閱者訂閱由GroupSourceOfEvents對象公開的事件。只要有任何一個包含的ISourceOfEvents對象引發事件,就會引發這些事件。

下面是我認爲可能解決你想要的兩種解決方案。這些解決方案使用事件訪問器(添加/刪除)。首先是一個簡單的例子,有很多陷阱。第二個是一個更復雜的例子,我認爲它解決了第一個例子的所有缺陷。

public class GroupSourceOfEvents : ISourceOfEvents 
{ 
    public IList<ISourceOfEvents> SourceOfEventsGroup { get; private set; } 

    public event Action<EventInfo1> SomethingHappened1 
    { 
     add 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened1 += value; 
      } 
     } 
     remove 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened1 -= value; 
      } 
     } 
    } 

    public event Action<EventInfo2> SomethingHappened2 
    { 
     add 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened2 += value; 
      } 
     } 
     remove 
     { 
      foreach (var s in SourceOfEventsGroup) 
      { 
       s.SomethingHappened2 -= value; 
      } 
     } 
    } 
    // {...} 

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) 
    { 
     SourceOfEventsGroup = sourceOfEventsGroup; 

     foreach (var s in SourceOfEventsGroup) 
     { 
      // {...} 
     } 
    } 
} 

,我能想到的主要缺陷是,你必須確保所有的ISourceOfEvents對象添加到您的GroupSourceOfEvents對象的任何用戶開始訂閱任何事件之前。這是因爲如果您從列表中添加ISourceOfEvents對象中的任何一個,則以前訂閱的任何監聽器都不會收到有關新添加對象的事件的通知。同樣,如果任何訂閱者在從列表中刪除任何ISourceOfEvents對象後取消訂閱GroupSourceOfEvents上的事件,那麼這些已移除對象上的事件將不會正確取消訂閱。另一個問題是事件的add/remove訪問器中沒有鎖定,如果涉及多線程,這可能是一個問題。

你可以用更復雜的相同方法來解決這些缺陷;你可以有一個與每個事件對應的私有委託變量,並且在每個事件的添加/刪除代碼中,你可以使用本地變量來存儲所有的訂閱者。然後,你必須避免公開公開你的SourceOfEventsGroup列表,並提供一些方法來添加/從列表中刪除,這樣一旦添加你就可以使用你的私有委託變量來訂閱新對象的事件,或者在刪除之後你可以使用私人委託變量取消訂閱已刪除的對象的事件。

下面是上述示例的另一個版本,它試圖解決我提到的問題。請注意,我已將該列表公開給列表IEnumerable<ISourceOfEvents>以避免公開暴露列表的添加/刪除功能。我添加了AddSource方法,以允許添加源並使用已註冊的訂閱者訂閱該源的事件。同樣,還有一個RemoveSource方法來處理從列表中移除源並取消訂閱其事件。還有鎖定來解決潛在的多線程問題。

public class GroupSourceOfEvents : ISourceOfEvents 
{ 
    private object SourceOfEventsGroupLock = new object(); 

    private IList<ISourceOfEvents> _SourceOfEventsGroup; 

    public IEnumerable<ISourceOfEvents> SourceOfEventsGroup 
    { 
     get { return _SourceOfEventsGroup; } 
    } 

    public void AddSource(ISourceOfEvents source) 
    { 
     lock (SourceOfEventsGroupLock) 
     { 
      source.SomethingHappened1 += _SomethingHappened1; 
      source.SomethingHappened2 += _SomethingHappened2; 
      _SourceOfEventsGroup.Add(source); 
     } 
    } 

    public void RemoveSource(ISourceOfEvents source) 
    { 
     lock (SourceOfEventsGroupLock) 
     { 
      source.SomethingHappened1 -= _SomethingHappened1; 
      source.SomethingHappened2 -= _SomethingHappened2; 
      _SourceOfEventsGroup.Remove(source); 
     } 
    } 

    private Action<EventInfo1> _SomethingHappened1; 
    public event Action<EventInfo1> SomethingHappened1 
    { 
     add 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened1 += value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened1 += value; 
       } 
      } 
     } 
     remove 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened1 -= value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened1 -= value; 
       } 
      } 
     } 
    } 

    private Action<EventInfo2> _SomethingHappened2; 
    public event Action<EventInfo2> SomethingHappened2 
    { 
     add 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened2 += value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened2 += value; 
       } 
      } 
     } 
     remove 
     { 
      lock (SourceOfEventsGroupLock) 
      { 
       _SomethingHappened2 -= value; 

       foreach (var s in SourceOfEventsGroup) 
       { 
        s.SomethingHappened2 -= value; 
       } 
      } 
     } 
    } 
    // {...} 

    public GroupSourceOfEvents(IList<ISourceOfEvents> sourceOfEventsGroup) 
    { 
     _SourceOfEventsGroup = sourceOfEventsGroup; 
    } 
} 

編輯:我試圖清理我的答案,重新排列段落,改寫句子,並刪除未完成的句子。內容保持不變。

相關問題