none
关于ObservableCollection的一个疑问,大家一起讨论讨论 RRS feed

  • 问题

  • 先上代码,再说疑问^_^

    class Program
        {
            static void Main(string[] args)
            {
                ObservableCollection<string> strs = new ObservableCollection<string>();
                strs.Add("123");
                strs.CollectionChanged += new NotifyCollectionChangedEventHandler(strs_CollectionChanged);
                strs.Clear();
     
                Console.Read();
            }
     
            private static void strs_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.NewItems != null)
                {
                    Console.WriteLine("NEW");
                }
     
                if (e.OldItems != null)
                {
                    Console.WriteLine("OLD");
                }
            }
        }

    运行上面的程序,我们可以看到,并没有任何输出。通过调试运行发现,Clear触发了CollectionChanged事件,但是OldItems的值却为null。为什么要这样,难道不应该给出集合中的所有值么?这算是微软类库的一个bug么,还是有意为之?

    2013年6月21日 8:08

答案

  • 这个集合最大的特色:凡是对于集合里边的元素进行属性操作(增、删、改和查),都会触发此事件。同样对类自身操作,也会引起事件的触发,下面给出源代码:

    using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Runtime; using System.Runtime.CompilerServices; using System.Threading; namespace System.Collections.ObjectModel { [__DynamicallyInvokable] [Serializable] [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged { private ObservableCollection<T>.SimpleMonitor _monitor; private const string CountString = "Count"; private const string IndexerName = "Item[]"; [__DynamicallyInvokable] public ObservableCollection() { } public ObservableCollection(List<T> list) : base((list != null ? new List<T>(list.Count) : list)) { this.CopyFrom(list); } [__DynamicallyInvokable] public ObservableCollection(IEnumerable<T> collection) { if (collection == null) { throw new ArgumentNullException("collection"); } this.CopyFrom(collection); } [__DynamicallyInvokable] protected IDisposable BlockReentrancy() { this._monitor.Enter(); return this._monitor; } [__DynamicallyInvokable] protected void CheckReentrancy() { if (this._monitor.Busy && this.CollectionChanged != null && (int)this.CollectionChanged.GetInvocationList().Length > 1) { throw new InvalidOperationException(SR.GetString("ObservableCollectionReentrancyNotAllowed")); } } [__DynamicallyInvokable] protected override void ClearItems() { this.CheckReentrancy(); base.ClearItems(); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionReset(); } private void CopyFrom(IEnumerable<T> collection) { IList<T> items = base.Items; if (collection != null && items != null) { foreach (T t in collection) { items.Add(t); } } } [__DynamicallyInvokable] protected override void InsertItem(int index, T item) { this.CheckReentrancy(); base.InsertItem(index, item); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); } [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public void Move(int oldIndex, int newIndex) { this.MoveItem(oldIndex, newIndex); } [__DynamicallyInvokable] protected virtual void MoveItem(int oldIndex, int newIndex) { this.CheckReentrancy(); T item = base[oldIndex]; base.RemoveItem(oldIndex); base.InsertItem(newIndex, item); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex); } [__DynamicallyInvokable] protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (this.CollectionChanged != null) { using (IDisposable disposable = this.BlockReentrancy()) { this.CollectionChanged(this, e); } } } private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index)); } private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex)); } private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index)); } private void OnCollectionReset() { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } [__DynamicallyInvokable] protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { if (this.PropertyChanged != null) { this.PropertyChanged(this, e); } } private void OnPropertyChanged(string propertyName) { this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } [__DynamicallyInvokable] protected override void RemoveItem(int index) { this.CheckReentrancy(); T item = base[index]; base.RemoveItem(index); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); } [__DynamicallyInvokable] protected override void SetItem(int index, T item) { this.CheckReentrancy(); T t = base[index]; base.SetItem(index, item); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, t, item, index); } [__DynamicallyInvokable] public virtual event NotifyCollectionChangedEventHandler CollectionChanged; [__DynamicallyInvokable] protected virtual event PropertyChangedEventHandler PropertyChanged; [__DynamicallyInvokable] event PropertyChangedEventHandler System.ComponentModel.INotifyPropertyChanged.PropertyChanged { [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] add { this.PropertyChanged += value; } [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] remove { this.PropertyChanged -= value; } } [Serializable] [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] private class SimpleMonitor : IDisposable { private int _busyCount; public bool Busy { get { return this._busyCount > 0; } } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public SimpleMonitor() { } public void Dispose() { ObservableCollection<T>.SimpleMonitor simpleMonitor = this; simpleMonitor._busyCount = simpleMonitor._busyCount - 1; } public void Enter() { ObservableCollection<T>.SimpleMonitor simpleMonitor = this; simpleMonitor._busyCount = simpleMonitor._busyCount + 1; } } } }

    从源代码中可以明显看出:调用父类的Clear其实本质是调用了Collection<T>中的Clear方法,而这个方法:

      public void Clear()
            {
                if (this.items.IsReadOnly)
                {
                    ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
                }
                this.ClearItems();
            }

    其中ClearItems是可以重载的:

     [__DynamicallyInvokable]
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
            protected virtual void ClearItems()
            {
                this.items.Clear();
            }

    明显已经被OberseverableCollection重载。

    [__DynamicallyInvokable] protected override void ClearItems() { this.CheckReentrancy(); base.ClearItems(); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionReset(); }

    那么:触发了OnCollectionReset函数:

    private void OnCollectionReset()
            {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }

    注意传入的参数:

    [__DynamicallyInvokable]
            public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action)
            {
                if (action != NotifyCollectionChangedAction.Reset)
                {
                    object[] objArray = new object[] { NotifyCollectionChangedAction.Reset };
                    throw new ArgumentException(SR.GetString("WrongActionForCtor", objArray), "action");
                }
                this.InitializeAdd(action, null, -1);
            }
    private void InitializeAdd(NotifyCollectionChangedAction action, IList newItems, int newStartingIndex)
            {
                IList lists;
                this._action = action;
                if (newItems == null)
                {
                    lists = null;
                }
                else
                {
                    lists = ArrayList.ReadOnly(newItems);
                }
                this._newItems = lists;
                this._newStartingIndex = newStartingIndex;
            }

    明显,newItems传入为null,自然lists,OldItems和NewItems均未赋值。都为null。

    关键在于这个类不是直接使用的,是被绑定到WPF控件上供它们内部使用的。


    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    • 已标记为答案 canglong13 2013年6月23日 10:34
    2013年6月22日 3:49
    版主

全部回复

  • 这个集合最大的特色:凡是对于集合里边的元素进行属性操作(增、删、改和查),都会触发此事件。同样对类自身操作,也会引起事件的触发,下面给出源代码:

    using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Runtime; using System.Runtime.CompilerServices; using System.Threading; namespace System.Collections.ObjectModel { [__DynamicallyInvokable] [Serializable] [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged { private ObservableCollection<T>.SimpleMonitor _monitor; private const string CountString = "Count"; private const string IndexerName = "Item[]"; [__DynamicallyInvokable] public ObservableCollection() { } public ObservableCollection(List<T> list) : base((list != null ? new List<T>(list.Count) : list)) { this.CopyFrom(list); } [__DynamicallyInvokable] public ObservableCollection(IEnumerable<T> collection) { if (collection == null) { throw new ArgumentNullException("collection"); } this.CopyFrom(collection); } [__DynamicallyInvokable] protected IDisposable BlockReentrancy() { this._monitor.Enter(); return this._monitor; } [__DynamicallyInvokable] protected void CheckReentrancy() { if (this._monitor.Busy && this.CollectionChanged != null && (int)this.CollectionChanged.GetInvocationList().Length > 1) { throw new InvalidOperationException(SR.GetString("ObservableCollectionReentrancyNotAllowed")); } } [__DynamicallyInvokable] protected override void ClearItems() { this.CheckReentrancy(); base.ClearItems(); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionReset(); } private void CopyFrom(IEnumerable<T> collection) { IList<T> items = base.Items; if (collection != null && items != null) { foreach (T t in collection) { items.Add(t); } } } [__DynamicallyInvokable] protected override void InsertItem(int index, T item) { this.CheckReentrancy(); base.InsertItem(index, item); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); } [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public void Move(int oldIndex, int newIndex) { this.MoveItem(oldIndex, newIndex); } [__DynamicallyInvokable] protected virtual void MoveItem(int oldIndex, int newIndex) { this.CheckReentrancy(); T item = base[oldIndex]; base.RemoveItem(oldIndex); base.InsertItem(newIndex, item); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex); } [__DynamicallyInvokable] protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (this.CollectionChanged != null) { using (IDisposable disposable = this.BlockReentrancy()) { this.CollectionChanged(this, e); } } } private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index)); } private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex)); } private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index)); } private void OnCollectionReset() { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } [__DynamicallyInvokable] protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { if (this.PropertyChanged != null) { this.PropertyChanged(this, e); } } private void OnPropertyChanged(string propertyName) { this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } [__DynamicallyInvokable] protected override void RemoveItem(int index) { this.CheckReentrancy(); T item = base[index]; base.RemoveItem(index); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); } [__DynamicallyInvokable] protected override void SetItem(int index, T item) { this.CheckReentrancy(); T t = base[index]; base.SetItem(index, item); this.OnPropertyChanged("Item[]"); this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, t, item, index); } [__DynamicallyInvokable] public virtual event NotifyCollectionChangedEventHandler CollectionChanged; [__DynamicallyInvokable] protected virtual event PropertyChangedEventHandler PropertyChanged; [__DynamicallyInvokable] event PropertyChangedEventHandler System.ComponentModel.INotifyPropertyChanged.PropertyChanged { [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] add { this.PropertyChanged += value; } [__DynamicallyInvokable] [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] remove { this.PropertyChanged -= value; } } [Serializable] [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")] private class SimpleMonitor : IDisposable { private int _busyCount; public bool Busy { get { return this._busyCount > 0; } } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public SimpleMonitor() { } public void Dispose() { ObservableCollection<T>.SimpleMonitor simpleMonitor = this; simpleMonitor._busyCount = simpleMonitor._busyCount - 1; } public void Enter() { ObservableCollection<T>.SimpleMonitor simpleMonitor = this; simpleMonitor._busyCount = simpleMonitor._busyCount + 1; } } } }

    从源代码中可以明显看出:调用父类的Clear其实本质是调用了Collection<T>中的Clear方法,而这个方法:

      public void Clear()
            {
                if (this.items.IsReadOnly)
                {
                    ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
                }
                this.ClearItems();
            }

    其中ClearItems是可以重载的:

     [__DynamicallyInvokable]
            [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
            protected virtual void ClearItems()
            {
                this.items.Clear();
            }

    明显已经被OberseverableCollection重载。

    [__DynamicallyInvokable] protected override void ClearItems() { this.CheckReentrancy(); base.ClearItems(); this.OnPropertyChanged("Count"); this.OnPropertyChanged("Item[]"); this.OnCollectionReset(); }

    那么:触发了OnCollectionReset函数:

    private void OnCollectionReset()
            {
                this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }

    注意传入的参数:

    [__DynamicallyInvokable]
            public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action)
            {
                if (action != NotifyCollectionChangedAction.Reset)
                {
                    object[] objArray = new object[] { NotifyCollectionChangedAction.Reset };
                    throw new ArgumentException(SR.GetString("WrongActionForCtor", objArray), "action");
                }
                this.InitializeAdd(action, null, -1);
            }
    private void InitializeAdd(NotifyCollectionChangedAction action, IList newItems, int newStartingIndex)
            {
                IList lists;
                this._action = action;
                if (newItems == null)
                {
                    lists = null;
                }
                else
                {
                    lists = ArrayList.ReadOnly(newItems);
                }
                this._newItems = lists;
                this._newStartingIndex = newStartingIndex;
            }

    明显,newItems传入为null,自然lists,OldItems和NewItems均未赋值。都为null。

    关键在于这个类不是直接使用的,是被绑定到WPF控件上供它们内部使用的。


    If you think one reply solves your problem, please mark it as An Answer, if you think someone's reply helps you, please mark it as a Proposed Answer

    Help by clicking:
    Click here to donate your rice to the poor
    Click to Donate
    Click to feed Dogs & Cats


    Found any spamming-senders? Please report at: Spam Report

    • 已标记为答案 canglong13 2013年6月23日 10:34
    2013年6月22日 3:49
    版主
  • CollectionChanged  这个事件在做Add操作时为什么不执行?
    2014年10月29日 7:04