两个类:
public class ObservableCollection<T>
{
public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged.Invoke(this, e);
}
}
public class DerivedObservableCollection<T> : ObservableCollection<T>
{
private bool _SuppressNotification;
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChangedMultiItem(
NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handlers = this.CollectionChanged;
if (handlers != null)
{
foreach (NotifyCollectionChangedEventHandler handler in
handlers.GetInvocationList())
{
if (handler.Target is CollectionView)
((CollectionView)handler.Target).Refresh();
else
handler(this, e);
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_SuppressNotification)
{
base.OnCollectionChanged(e);//<------is it usefull?------------
if (CollectionChanged != null)
CollectionChanged.Invoke(this, e);
}
}
}
可以在DerivedObservableCollection实例(在DerivedObservableCollection之外)的ObservableCollection.CollectionChanged上订阅吗? 或致电:
base.OnCollectionChanged(e);
?
实际上,我遇到了System.Collections.ObjectModel.ObservableCollection和这个answer的问题。
答案 0 :(得分:3)
在c#中,不应将事件声明为virtual
。相反,当您在类中声明事件时,最佳实践是还提供引发该事件的protected virtual
方法-这就是您在问题中提供的代码中的方法OnCollectionChanged
。
(顺便说一句,请注意,如果没有人在听此事件,则此方法将引发异常-您应确保它不为null-因此将CollectionChanged.Invoke(this, e);
更改为CollectionChanged?.Invoke(this, e);
。)
这样做的原因是c#编译器无法正确处理虚拟事件,如How to: Raise Base Class Events in Derived Classes (C# Programming Guide)中所述(您可以感谢@ itsme86在问题注释中的链接):
注意
不要在基类中声明虚拟事件,而在派生类中重写它们。 C#编译器无法正确处理这些事件,并且无法确定派生事件的订阅者是否实际上将在订阅基类事件。
只能从声明它的类内部引发一个事件,这就是为什么您需要
protected virtual
方法从派生类中引发它-也记录在同一页中:
当创建一个可用作其他类的基类的类时,应考虑以下事实:事件是一种特殊类型的委托,只能从声明它们的类中调用。派生类无法直接调用基类中声明的事件。
正确的解决方案是不覆盖事件,而仅覆盖引发事件的方法-也就是说,当您想要更改引发事件的条件时,可以通过调用base.OnCollectionChanged(e);
来引发事件。
正确的代码实现如下所示:
public class ObservableCollection<T>
{
// Note: the event is no longer virtual
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
}
public class DerivedObservableCollection<T> : ObservableCollection<T>
{
private bool _SuppressNotification;
// This line is removed:
// public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChangedMultiItem(
NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handlers = this.CollectionChanged;
if (handlers != null)
{
foreach (NotifyCollectionChangedEventHandler handler in
handlers.GetInvocationList())
{
if (handler.Target is CollectionView)
((CollectionView)handler.Target).Refresh();
else
handler(this, e);
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_SuppressNotification)
{
// This is where you raise the event
base.OnCollectionChanged(e);
// the next two lines are also removed, since you've already raised the event
// and you can't raise it directly from the derived class anyway
//if (CollectionChanged != null)
// CollectionChanged.Invoke(this, e);
}
}
}