为什么 ObservableCollection<T> 实现 INotifyPropertyChanged?

Why does ObservableCollection<T> implement INotifyPropertyChanged?

在 .NET 4.0 中,ObservableCollection<T> 没有定义单个 属性,也没有覆盖其父级或接口的任何 属性。那么为什么 ObservableCollection<T> 实现 INotifyPropertyChanged?

我能想到的一个原因是它让子类更容易定义自己的属性并使用ObservableCollection<T>实现的OnPropertyChanged方法。但这是主要原因吗?

CountItem[] 更改都会收到通知。这是一个示例(仅使用 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

属性ItemItemsCount中,只有Item实际上有一个setter,所以不需要重写ItemsCount 由于您无法设置它们,因此无需从中引发事件。它们只会响应某些其他方法(如 AddRemove)而更改,并且 那些 方法将引发必要的 属性 更改事件(实际上如果您查看源代码,ObservableCollection<T> 也没有覆盖这些方法,而是覆盖了由 AddRemove 在基础 class 中调用的 protected 方法]).

现在对于 Item,属性 没有被覆盖,但是如果您查看 SetItem 方法的 source

/// <summary>
/// Called by base class Collection&lt;T&gt; 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 事件。