2010-08-02 42 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; 
    } 
} 

编辑:我试图清理我的答案,重新排列段落,改写句子,并删除未完成的句子。内容保持不变。