挂钩列表中所有对象的事件

Nat*_*han 6 .net c# reflection

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

期望的解决方案:所以,我正在尝试提供类似于EventBindingList包含可挂钩对象的东西,并允许用户一次挂钩多个对象,以及添加和删除列表中的对象.

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

    private EventInfo _info;

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

    public EventBindingList(EventInfo info)
    {
        _info = info;
    }
Run Code Online (Sandbox Code Playgroud)

我尝试了几种方法,但我仍然遇到方法,委托,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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

解决方案2失败:

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

请求: 关于如何实现这一点还有其他想法吗?

age*_*t-j 5

编辑:

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));
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

原始答案:可以使用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);
   }
}
Run Code Online (Sandbox Code Playgroud)

然后,您的派生类型可以做到这一点:

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){...}
}
Run Code Online (Sandbox Code Playgroud)

您也可以根据需要订阅反射,但这会更容易出错,并且不易阅读/维护/理解。