【问题标题】:C#: ObservableCollection - why virtual “CollectionChanged” event?C#:ObservableCollection - 为什么是虚拟“CollectionChanged”事件?
【发布时间】:2014-04-22 22:00:53
【问题描述】:

为什么 CollectionChanged 事件是ObservableCollection 中的虚拟?我们有虚拟的OnCollectionChanged 方法,应该足以覆盖事件调用吧?

我看不出这个有什么用,而且虚拟事件也是邪恶的。虚拟事件的笨拙使用会带来很多逻辑问题,但是虚拟事件即使在框架中也存在。

这只是糟糕的设计还是有人在实际中使用它?

【问题讨论】:

  • ObservableCollection 是一个基类,不 使事件virtual 是一个糟糕的设计。为什么你认为相反?
  • 乔恩,假设有人在派生类中重写了 CollectionChanged 事件,并让 OnCollectionChanged 方法保持不变。这使得每个订阅(甚至 wpf 引擎)都进入派生类的 CollectionChanged 事件,这会破坏方法 OnCollectionChanged,因为它会触发为空的基本事件
  • 所以派生类的作者如果不注意自己在做什么,就可以写出bug。这很令人惊讶吗?是派生作者的错还是基础作者的错? object.GetHashCode() 是否应该不是虚拟的,因为您可以覆盖它而忘记覆盖 Equals?
  • 是的,但如果没有强有力的理由,我宁愿不使用虚拟事件。在这里我看不出任何原因,因为通过重写 OnCollectionChanged 方法可以达到相同的逻辑
  • 我们是否在讨论您的偏好或设计决策?如果事件不是virtual,则派生类无法更改其底层表示,并且如果他们决定要检查订阅者,它们也无法检查。 “到目前为止我还不需要它”不是一个论点。

标签: wpf observablecollection


【解决方案1】:

我们可以就基类和设计进行辩论,但这不是直接/学术的答案,而是更多的示例。我个人觉得可以扩展 ObservableCollection 并覆盖 OnCollectionChanged 非常棒。 ObservableCollection 非常健谈,每次添加/删除项目时,它都会用属性更改消息轰炸 UI 线程并减慢它的速度(例如,在数据网格中,要更新的每个绑定)。因此,据我所知,许多人扩展 ObservableCollection 以抑制此类通知,直到他们完成添加项目。仅仅因为 WPF 控制 DataGrids/ListViews 等。响应 CollectionChanged 就可以了。

这里是用法,我刷新我的数据,而不是一次添加一个项目,我填充一个列表,然后我只用它重置 ObservableCollection 一次,这极大地加快了 UI 响应速度:

private void OnExecuteRefreshCompleted(IEnumerable<MyObject> result)
{
UiUtilities.OnUi(() => { _myObservableCollectionField.Reset(result, true);              
        });

这是我的扩展类:

public class ObservableCollectionExtended<T> : ObservableCollection<T>
{
    private bool _suppressNotification;

            //without virtual , I couldn't have done this override
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (!_suppressNotification)
            base.OnCollectionChanged(e);
    }

    public void Clear(bool suppressNotificationUntillComplete)
    {
        _suppressNotification = suppressNotificationUntillComplete;

        Clear();

        _suppressNotification = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public void ClearItems(bool suppressNotificationUntillComplete)
    {
        _suppressNotification = suppressNotificationUntillComplete;

        base.ClearItems();

        _suppressNotification = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public void AddRange(IEnumerable<T> list, bool suppressNotificationUntillComplete)
    {
        if (list == null)
            throw new ArgumentNullException("list");

        _suppressNotification = suppressNotificationUntillComplete;

        foreach (T item in list)
            Add(item);

        _suppressNotification = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    /// <summary>
    /// clears old items, and new ones
    /// </summary>
    /// <param name="list"></param>
    /// <param name="suppressNotificationUntillComplete"></param>
    public void Reset(IEnumerable<T> list, bool suppressNotificationUntillComplete)
    {
        if (list == null)
            throw new ArgumentNullException("list");

        _suppressNotification = suppressNotificationUntillComplete;

        Clear();

        foreach (T item in list)
            Add(item);

        _suppressNotification = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

【讨论】:

    【解决方案2】:

    恐怕丹尼斯没有回答“为什么 CollectionChanged 事件是虚拟的?”这个问题。而是问题“为什么 OnCollectionChanged() 方法是虚拟的?”。第一个问题由乔恩回答得更恰当。您可能对像我前段时间那样处理订阅者的不同方式感兴趣。 让我们有两种不同的场景:

    第一个场景: 我想引发 CollectionChanged 事件,并确保在此事件中调用的任何委托(委托列表)在发生异常时不会中断以下委托的调用。换句话说,一个事件是由一个代表列表组成的。如果我假设有 10 个订阅者(代表)并且第三个代表提出了异常,我可以继续调用其余的代表。标准实现中断调用。

    第二种情况: 我可能想让一些订阅者被优先考虑(他们更早收到事件),即使他们比其他订阅者晚。在“添加”事件中,我可以在我的自定义委托列表中将一些特定的订阅者移动到更低或更高的位置,稍后用于引发事件..

    【讨论】:

    • 我认为在这两种情况下创建新事件会更好,因为您正在添加您的班级用户的 CollectionChanged 事件不期望的逻辑......所以会让他们感到困惑......跨度>
    猜你喜欢
    • 2012-03-11
    • 2013-01-12
    • 1970-01-01
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    相关资源
    最近更新 更多