何时使用 ObservableCollection 与 AdvisableCollection w/ PostSharp NotifyPropertyChanged?

When to use ObservableCollection vs AdvisableCollection w/ PostSharp NotifyPropertyChanged?

这是 PostSharp support discussion(标题:"NotifyPropertyChanged and ObservableCollection"),它引起了我最初对此主题的困惑。进一步的解释在我发布的答案中(不是 100% 相信我的答案)。

我对 PostSharp 的 [NotifyPropertyChanged] 方面如何影响 class 的 collection 属性(如果有的话)以及我是否需要使用 ObservableCollection<>AdvisableCollection<>。如果我理解正确,如果我想要来自 collection 的通知,我必须至少将其更改为 ObservableCollection<>,对吗? [NotifyPropertyChanged] 方面不会神奇地使 class 中的任何 collection 类型成为可观察的。

如果是这样,那我什么时候使用ObservableCollection<>AdvisableCollection<>?当我需要 PostSharp 应用的聚合模式时,是否应该保留 AdvisableCollection<>?或者我应该在应用 PostSharp 的 [NotifyPropertyChanged] 属性时总是使用 AdvisableCollection<> 吗?

[NotifyPropertyChanged]
public class Test {
    public int PropVal { get; set; }
    public List<string> PropCollection { get; set; } //Should this be ObservableCollection
                                                    //or AdvisableCollection?
}

好吧,在收到关于这个问题的 "Tumbleweed" 徽章后(不是有意的,但很有趣......比反对票更好),我花了更多时间试图对 out/better 定义我的混乱并从混乱中合并我自己的答案...

最初引起我困惑的 PostSharp support discussion(标题:"NotifyPropertyChanged and ObservableCollection")提到了两件事:

-在 4.2 中添加了对 ObservableCollection<> 中拦截更改的支持。

-要在每个 collection 更改时引发 PropertyChanged 事件,collection 必须标记为 [AggregateAllChanges]

因此,如果应用 [AggregateAllChanges] 属性,PostSharp 的 NotifyPropertyChanged 方面似乎至少支持 ObservableCollection<>。然而,随后在 PostSharp 网站的聊天功能上的对话产生了:

-"You need to use collection classes defined by PostSharp to have reliable support for NotifyPropertyChanged aspect. See Working With Collections"(其中提到了可聚合模式)。

-阅读了提供的文档后,结论似乎是我需要使用AdvisableCollection<>(一个PostSharp-defined collection)来代替,但是示例似乎都在可聚合模式的上下文,具有 parent-child 关系等。因此我感到困惑。我不是想建立 parent-child 关系,只是想在 collection 上获得 NotifyPropertyChange

接下来是另一个 support discussion,它演示了将 NotifyPropertyChanged 与非 PostSharp-defined collection 一起使用,将 [AggregateAllChanges] 应用于 BindingList<>

-在这种情况下,BindingList<> 没有 NotifyPropertyChanged 预期的事件之一,因此必须使用 NotifyPropertyChangedServices 手动连接。

为了了解更多背景知识,我阅读了这个 PostSharp blog post 进入聚合和组合模式。

试图归结所有内容,我的(可能不正确的)结论是:

-[AggregateAllChanges] 应用于非 PostSharp-defined collections (ObservableCollection<>, BindingList<>) 实际上是一个 opt-in 到 NotifyPropertyChanged 方面,需要注意的是 collection 你应用它(例如 BindingList<>)可能不支持方面期望的所有事件,需要你连接PropertyChanged 自己通过 NotifyPropertyChangedServices.

通知

-AdvisableCollection<> 实现了 INotifyPropertyChanged 以及 INotifyCollectionChanged(和 ObservableCollection<> 一样),可以用来代替标准的 .net collections(不是 BindingList<> 虽然...不同的接口),并且仍然需要 [AggregateAllChanges] 来引发 PropertyChanged 通知。否则,AdvisableCollection<> 的目的是允许注入与可聚合模式相关的行为。

如果有人对这些假设有任何更正,请随时:) 我很乐意将 clearer/better 解释标记为答案。

祝贺您获得 Tumbleweed 徽章,对于延迟回复深表歉意。

集合目前没有很好地与方面集成,因为现实生活中的示例需要一些工作才能正常工作。让我解释几个关键步骤:

[AggregateAllChanges] 指示运行时它应该将在集合上观察到的任何更改作为 属性 本身的更改进行中继。这特别意味着创建了一个虚拟 Item[] 依赖项("standard" 集合使用它来通知浅集合 "state" 的更改)。

实际上,与集合内容一起使用的属性在某种意义上通常是聚合,不仅取决于存储在集合中的项目集,还取决于集合中各个对象的状态。目前无法表达 and/or 有选择地转发这些更改。为此,您需要创建一个从 ObservableCollection<T> 派生的 class,如下所示:

[NotifyPropertyChanged]
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            foreach (T item in e.OldItems)
            {
                ((INotifyPropertyChanged)item).PropertyChanged -= OnItemPropertyChanged;
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Add)
        {
            foreach (T item in e.NewItems)
            {
                ((INotifyPropertyChanged)item).PropertyChanged += OnItemPropertyChanged;
            }
        }

        base.OnCollectionChanged(e);
    }

    protected void OnPropertyChanged(string propertyName)
    {
        base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        NotifyPropertyChangedServices.SignalPropertyChanged(this, "Item[]");

        NotifyCollectionChangedEventArgs collectionChangedEventArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        base.OnCollectionChanged(collectionChangedEventArgs);
    }
}

上面的内容正是需要的 - 当集合中的任何对象发生任何变化时,集合报告它本身已经改变。

现在在 class 中它看起来像这样:

[NotifyPropertyChanged]
public class TestClass
{
    [AggregateAllChanges]
    public ObservableCollectionEx<TestItem> Items { get; } = new ObservableCollectionEx<TestItem>();

    [SafeForDependencyAnalysis]
    public int Sum
    {
        get
        {
            if (Depends.Guard)
            {
                Depends.On(this.Items);
            }

            return this.Items.Sum(x => x.Value);
        }
    }
}

[NotifyPropertyChanged]
public class TestItem
{
    public int Value { get; set; }
}

如果 TestItem class 上还有经常更改的其他属性,则上述内容在性能方面并不理想。在那种情况下,人们可能会考虑向 ObservableCollectionEx.

添加某种过滤

关于 AdvisableCollection<T> 你是对的 - 虽然它可以使用,但它的主要目的是不同的。