挂接到列表中所有对象的事件



问题:我发现自己经常想处理一组对象上的事件。在从集合中添加和删除对象时,必须挂起或取消挂起每个对象。我发现用相同的事件挂钩代码设计每个这样做的类是乏味和重复的。

所需解决方案:因此,我正在尝试想出类似EventBindingList的东西,它包含可挂起的对象,允许用户一次挂起多个对象,并在列表中添加和删除对象。

为了保持它的通用性,有必要使用反射。在列表的构造函数中,用户可以通过EventInfo或事件名称指定要挂接的事件。这似乎是最简单的方法。

    private EventInfo _info;
    public EventBindingList(string EventName)
    {
        _info = typeof(T).GetEvents().Where(e => e.Name == EventName).First();
    }
    public EventBindingList(EventInfo info)
    {
        _info = info;
    }

我尝试了几种方法,但方法、委托、lambdas和EventHandlers之间的差异仍然存在问题。

失败的解决方案1:

我尝试过的一个解决方案是使用自定义事件访问器,但没有成功。这将是包含要挂接的对象的列表中的一个事件。这是因为,在添加EventHandler时,会引发ArgumentException:Object of type 'System.EventHandler' cannot be converted to type 'ExternalProject.CustomEventHandler'.我尝试将EventHandler强制转换为正确的类型(使用泛型类型参数,因为这是外部项目的事件处理程序(,但强制转换会失败。

    public event EventHandler ElementEvent
    {
        add
        {
            _handlers.Add(value);
            foreach (T t in this)
            {
                _info.AddEventHandler(t, value);
            }
        }
        remove
        {
            foreach (T t in this)
            {
                _info.RemoveEventHandler(t, value);
            }
            _handlers.Remove(value);
        }
    }

失败的解决方案2:

我没有找到一个好的方法让列表本身处理事件,然后为任何订阅者调用委托。我发现尝试使用反射添加事件处理程序需要一个委托。在我的测试中,我无法找到保存事件参数的方法,也无法将这些参数传递给订阅者。

请求:对于如何实现这一目标,还有其他想法吗?

编辑:

public abstract class ManagedEventCollection<T,TEventArgs> : IList<T>
{
   private EventInfo m_event;
   public ManagedEventCollection(string eventName)
   {
      m_list = new ObservableCollection<T> ();
      m_list.CollectionChanged += CollectionChanged;
      m_event = typeof (T).GetEvent (eventName);
   }
   // Add/Remove/Indexer/Clear methods alter contents of m_list.
   public EventHandler<TEventArgs> Handler{get;set;}
   protected abstract void OnItemAdded(T item);
   protected abstract void OnItemRemoved(T item);
   private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs ea)
   {
      foreach (T item in ea.NewItems)
      {
         m_event.AddEventHandler (
            item, 
            Delegate.CreateDelegate (m_event.EventHandlerType, item, Handler.Method));
      }
      foreach (T item in ea.OldItems)
      {
         m_event.RemoveEventHandler (
            item, 
            Delegate.CreateDelegate (m_event.EventHandlerType, item, Handler.Method));
      }
   }
}

原始答案:您可以使用ObservableCollection<T>。此类有一个CollectionChanged事件,您可以根据需要在其中订阅/取消订阅事件。

我会创建一个基类(这是从内存中获得的,只是为了获得交叉点(。

public abstract class ManagedEventCollection<T> : IList<T>
{
   public ManagedEventCollection()
   {
      m_list = new ObservableCollection<T> ();
      m_list.CollectionChanged += CollectionChanged;
   }
   ... // Add/Remove/Indexer/Clear methods alter contents of m_list.
   protected abstract void OnItemAdded(T item);
   protected abstract void OnItemRemoved(T item);
   private ObservableCollection<T> m_list;
   private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs ea)
   {
      foreach (T item in ea.NewItems)
         OnItemAdded(item);
      foreach (T item in ea.OldItems)
         OnItemRemoved(item);
   }
}

然后,你的派生类型可以这样做:

public class DogManagedEventCollection : ManagedEventCollection<Dog>
{
   protected override OnItemAdded (Dog dog)
   {
      dog.Bark += Bark;
   }
   protected override OnItemRemoved (Dog dog)
   {
      dog.Bark -= Bark;
   }
   private void Bark(object sender, BarkEventArgs ea){...}
}

如果你真的想订阅,你也可以订阅反射,但这更容易出错,而且不容易阅读/维护/理解。

我还没有测试过,因为我现在还不能,所以请小心:(。

如果出于某种原因ObservableCollection不是你想要的(顺便说一句,我认为这是一个比我的更好、更清洁的解决方案(,这样的东西难道不管用吗?

public class MyEventList<TElementType,TEventArgType>: IList<TElementType> where TEventArgType: EventArgs
{
    private EventInfo eventInfo;
    private EventHandler<TEventArgType> eventHandler;
    public MyEventList(string eventName, EventHandler<TEventArgType> eventHandler)
    {
        if (eventHandler == null)
            throw new ArgumentNullException("eventHandler");
        if (eventName == null)
            throw new ArgumentNullException("eventName");
        this.eventInfo = typeof(TElementType).GetEvent(eventName);
        if (this.eventInfo == null)
            throw new ArgumentException("Specified event not found.", "eventName");
        if (this.eventInfo.EventHandlerType != eventHandler.GetType())
            throw new ArgumentException("EventHandler type does not match specified event.", "eventHandler");
        this.eventHandler = eventHandler;
    }
    public void Add(TElementType item)
    {
        ...
        eventInfo.AddEventHandler(item, this.eventHandler);
        ...
    }
    public bool Remove(TElementType item)
    {
        ...
        eventInfo.RemoveEventHandler(item, this.eventHandler);
        ...
    }
    ...
 }

相关内容

  • 没有找到相关文章

最新更新