使用继承事件的调用列表是一个坏主意吗?



我有一个继承自ObservableCollection<T>的类。 在该类中,我有一个在内部更改集合的方法,并且我想为此抑制CollectionChanged事件。

public class ContentBlockList : ObservableCollection<int> {
    public void SomeMethod() {
        var handlers = CollectionChanged.GetInvocationList();
        foreach (NotifyCollectionChangedEventHandler handler in handlers) {
            CollectionChanged -= handler;
        }
        // do stuff here
        foreach (NotifyCollectionChangedEventHandler handler in handlers) {
            CollectionChanged += handler;
        }
    }
}

直观地说,这似乎应该有效,因为我正在从其包含对象中访问该事件。 不幸的是,编译器说

事件"ObservableCollection.CollectionChanged"只能 出现在 += 或 -= 的左侧

如果我覆盖CollectionChangedOnCollectionChanged(),我可以让代码工作,本质上是用我自己的副本替换 .NET 版本。 然而,不得不做这样的事情让我怀疑我忽略了为什么这样做首先是一个坏主意的一些原因。 感谢您对此的任何想法。

由于取消订阅和重新订阅事件是一个相对(不是很痛苦,但我不知道可能有多少订阅者)缓慢的过程,我建议您研究覆盖基本ObservableCollectionOnCollectionChangedOnPropertyChanged方法。

所以有一些类似于:

public class ContentBlockList : ObservableCollection<int>
{
    private bool internallyUpdating;
    public void SomeMethod()
    {
        this.internallyUpdating = true;
        // Do Stuff (Add to base collection)
        this.internallyUpdating = false;
        this.OnPropertyChanged(new PropertyChangedEventArgs(@"Count");
        this.OnPropertyChanged(new PropertyChangedEventArgs(@"Item[]");
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if(this.internallyUpdating)
        {
            return;
        }
        base.OnCollectionChanged(e);
    }
    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if(this.internallyUpdating)
        {
            return;
        } 
        base.OnPropertyChanged(e);
    }
}

这允许能够在内部更新时抑制引发的事件,但这样做的方式意味着您不必取消订阅和重新订阅事件。

当正常添加到此集合时(即使用 contentBlockList.Add(1) ),您将直接调用基事件。但是,当您尝试在内部更新时,您将抑制这些事件,直到完成。我想说这在性能方面更有效,但也比你所看到的更整洁的代码。

最后一点,我还要说您提供的NotifyCollectionChangedEventActionReset。您可能已经对集合进行了相当大的更改,为了处理它,您希望任何订阅者都必须刷新其对集合的外观,无论是 WPF 视图中的控件还是使用该集合的另一个类。

最好使用这个:

public class ContentBlockList : ObservableCollection<int>
    {
        ContentBlockList()
        {
            this.CollectionChanged += ContentBlockList_CollectionChanged;
        }
        void ContentBlockList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        }
    }

如果您维护代码,请尝试此操作

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        ContentBlockList pp = new ContentBlockList();
        pp.CollectionChanged += pp_CollectionChanged;
        pp.CollectionChanged += pp_CollectionChanged1; 
        pp.Add(11112);
        pp.SomeMethod();
    }
    void pp_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
    }
    void pp_CollectionChanged1(object sender, NotifyCollectionChangedEventArgs e)
    {
    }

}
public class ContentBlockList : ObservableCollection<int>
{
    public void SomeMethod()
    {
        var handlers = CollectionChanged.GetInvocationList();
        foreach (NotifyCollectionChangedEventHandler handler in handlers)
        {
            CollectionChanged -= handler;
        }
        // do stuff here
        foreach (NotifyCollectionChangedEventHandler handler in handlers)
        {
            CollectionChanged += handler;
        }
    }
    public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
}

据我了解,您需要中断CollectionChanged的触发以静默地做一些工作。因此,您可以创建像__FireCollectionChanged这样的布尔字段,然后覆盖OnCollectionChanged()执行以下操作:

protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
    if (__FireCollectionChanged)
        base.OnCollectionChanged(e);
}

然后,您可以控制事件是否由该布尔字段触发。

并回答实际问题:您不能直接使用调用列表,因为事件不是委托类型字段。它基本上只是订阅/取消订阅行为的两种方法addremove。基础委托字段是在后台创建的,您通常不想使用它。

最新更新