何时使用 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>
你是对的 - 虽然它可以使用,但它的主要目的是不同的。
这是 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>
你是对的 - 虽然它可以使用,但它的主要目的是不同的。