为什么INotifyCollectionChanged不扩展IList?

时间:2015-01-14 09:23:46

标签: c# .net collections

我曾经遇到过几次我希望通过INotifyCollectionChanged界面观察集合的情况,但也希望能够访问任何集合的元素。 INotifyCollectionChanged接口不提供任何访问元素的方法,除了那些参与更改事件的元素(通常)包含在NotifyCollectionChangedEventArgs中)。

现在我的想法是:

  1. 我们知道任何工具INotifyCollectionChanged都是一个集合(呃)。
  2. 由于NotifyPropertyChangedEventArgs包含指示更改位置的索引,因此我们知道可以通过索引访问这些元素。
  3. 可以通过索引访问的集合是一个列表,因此似乎要求任何INotifyCollectionChanged实现者​​也实现IList是有意义的。这可以通过INotifyCollectionChanged扩展IList来轻松完成。

    有谁知道为什么不是这种情况?

2 个答案:

答案 0 :(得分:3)

我认为您需要查找SOLID软件设计原则,特别是Liskov Substitution Principle

您问为什么INotifyCollectionChanged接口不会扩展IList接口。让我用Liskov Subsitution Principle的反问题来回答:

  

我可以说INotifyCollectionChangedIList吗?

不,我不这么认为,原因如下:

  1. INotifyCollectionChanged表达了实现此接口的类需要通知其用户是否更改了其基础集合,该基础集合是IList还是ICollection,或者即使IEnumerable,我们也不知道。这是IList界面的不同概念,它只是ICollection,具有公开的indexer

  2. 您提到NotifyPropertyChangedEventArgs(我认为您的意思是NotifyCollectionChangedEventArgs)会公开索引的属性,指示集合的更改位置。但是,这并不意味着这些属性必须通过IList的索引器公开项目。它可以是一个任意数字,一个魔法常数,无论如何。由实现类决定如何公开索引。

  3. 为了演示这一点,请查看我实现INotifyCollectionChanged的自定义类:

    public class MyCustomCollection : INotifyCollectionChanged
    {
        // This is what I meant by the "underlying collection", can be replaced with
        // ICollection<int> and it will still work, or even IEnumerable<int> but with some
        // code change to store the elements in an array
        private readonly IList<int> _ints;
    
        public MyCustomCollection()
        {
            _ints = new List<int>();
        }
    
        public event NotifyCollectionChangedEventHandler CollectionChanged;
    
        public void AddInt(int i)
        {
            _ints.Add(i);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(
                NotifyCollectionChangedAction.Move, 
                (IList)_ints, 
                _ints.Count,
                _ints.Count - 1));
        }
    
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var handler = CollectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }
    

    希望这能回答你的问题。

答案 1 :(得分:1)

我不知道为什么,但我看到了这个决定的原因(不知道决定 - 这只是我的意见):

  1. 以这种方式实现INotifyCollectionChanged遵循角色接口的原则(描述更改通知而不是对象)请参阅@rexcfnghk帖子!

  2. 只有EventArgs中更改的元素表示为IList<T>,这并不意味着集合本身需要是列表

  3. 总而言之,他们决定使用允许更改通知的Collection<T>来创建默认集合。