为什么 ObservableCollection<T> 实现 INotifyPropertyChanged?
Why does ObservableCollection<T> implement INotifyPropertyChanged?
在 .NET 4.0 中,ObservableCollection<T>
没有定义单个 属性,也没有覆盖其父级或接口的任何 属性。那么为什么 ObservableCollection<T>
实现 INotifyPropertyChanged
?
我能想到的一个原因是它让子类更容易定义自己的属性并使用ObservableCollection<T>
实现的OnPropertyChanged
方法。但这是主要原因吗?
Count
和 Item[]
更改都会收到通知。这是一个示例(仅使用 C# 6 进行字符串插值):
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
class Test
{
static void Main(string[] args)
{
var collection = new ObservableCollection<string>();
((INotifyPropertyChanged)collection).PropertyChanged += (sender, e) =>
{
Console.WriteLine($" {e.PropertyName} changed");
};
Console.WriteLine("Adding");
collection.Add("Item");
Console.WriteLine("Adding");
collection.Add("Other item");
Console.WriteLine("Removing");
collection.RemoveAt(0);
Console.WriteLine("Changing");
collection[0] = "Different";
}
}
输出:
Adding
Count changed
Item[] changed
Adding
Count changed
Item[] changed
Removing
Count changed
Item[] changed
Changing
Item[] changed
属性Item
、Items
和Count
中,只有Item
实际上有一个setter,所以不需要重写Items
或 Count
由于您无法设置它们,因此无需从中引发事件。它们只会响应某些其他方法(如 Add
或 Remove
)而更改,并且 那些 方法将引发必要的 属性 更改事件(实际上如果您查看源代码,ObservableCollection<T>
也没有覆盖这些方法,而是覆盖了由 Add
和 Remove
在基础 class 中调用的 protected
方法]).
现在对于 Item
,属性 没有被覆盖,但是如果您查看 SetItem
方法的 source:
/// <summary>
/// Called by base class Collection<T> when an item is set in list;
/// raises a CollectionChanged event to any listeners.
/// </summary>
protected override void SetItem(int index, T item)
{
CheckReentrancy();
T originalItem = this[index];
base.SetItem(index, item);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
}
您会从评论中注意到,当设置 Item
时,基础 class 会调用它,您还会注意到它会触发 OnPropertyChanged
事件.
如果您查看 Collection<T>
的 source,您可以确认是这种情况:
public T this[int index] {
#if !FEATURE_CORECLR
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
get { return items[index]; }
set {
if( items.IsReadOnly) {
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
}
if (index < 0 || index >= items.Count) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
SetItem(index, value);
}
}
所以总而言之,Collection<T>
中的 Item
调用 SetItem
,它在 ObservableCollection<T>
中被覆盖,触发了 PropertyChanged
事件。
在 .NET 4.0 中,ObservableCollection<T>
没有定义单个 属性,也没有覆盖其父级或接口的任何 属性。那么为什么 ObservableCollection<T>
实现 INotifyPropertyChanged
?
我能想到的一个原因是它让子类更容易定义自己的属性并使用ObservableCollection<T>
实现的OnPropertyChanged
方法。但这是主要原因吗?
Count
和 Item[]
更改都会收到通知。这是一个示例(仅使用 C# 6 进行字符串插值):
using System;
using System.ComponentModel;
using System.Collections.ObjectModel;
class Test
{
static void Main(string[] args)
{
var collection = new ObservableCollection<string>();
((INotifyPropertyChanged)collection).PropertyChanged += (sender, e) =>
{
Console.WriteLine($" {e.PropertyName} changed");
};
Console.WriteLine("Adding");
collection.Add("Item");
Console.WriteLine("Adding");
collection.Add("Other item");
Console.WriteLine("Removing");
collection.RemoveAt(0);
Console.WriteLine("Changing");
collection[0] = "Different";
}
}
输出:
Adding
Count changed
Item[] changed
Adding
Count changed
Item[] changed
Removing
Count changed
Item[] changed
Changing
Item[] changed
属性Item
、Items
和Count
中,只有Item
实际上有一个setter,所以不需要重写Items
或 Count
由于您无法设置它们,因此无需从中引发事件。它们只会响应某些其他方法(如 Add
或 Remove
)而更改,并且 那些 方法将引发必要的 属性 更改事件(实际上如果您查看源代码,ObservableCollection<T>
也没有覆盖这些方法,而是覆盖了由 Add
和 Remove
在基础 class 中调用的 protected
方法]).
现在对于 Item
,属性 没有被覆盖,但是如果您查看 SetItem
方法的 source:
/// <summary>
/// Called by base class Collection<T> when an item is set in list;
/// raises a CollectionChanged event to any listeners.
/// </summary>
protected override void SetItem(int index, T item)
{
CheckReentrancy();
T originalItem = this[index];
base.SetItem(index, item);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
}
您会从评论中注意到,当设置 Item
时,基础 class 会调用它,您还会注意到它会触发 OnPropertyChanged
事件.
如果您查看 Collection<T>
的 source,您可以确认是这种情况:
public T this[int index] {
#if !FEATURE_CORECLR
[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
#endif
get { return items[index]; }
set {
if( items.IsReadOnly) {
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
}
if (index < 0 || index >= items.Count) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
SetItem(index, value);
}
}
所以总而言之,Collection<T>
中的 Item
调用 SetItem
,它在 ObservableCollection<T>
中被覆盖,触发了 PropertyChanged
事件。