问题:我发现自己经常想处理一组对象上的事件。在从集合中添加和删除对象时,必须挂起或取消挂起每个对象。我发现用相同的事件挂钩代码设计每个这样做的类是乏味和重复的。
所需解决方案:因此,我正在尝试想出类似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);
...
}
...
}