C# 为什么';t inotifycollection是否已更改?

C# 为什么';t inotifycollection是否已更改?,c#,.net,collections,C#,.net,Collections,我曾多次遇到这样的情况:我希望通过INotifyCollectionChanged界面观察一个集合,但也希望能够访问该集合的任何元素。INotifyCollectionChanged接口不提供任何访问元素的方法,但与更改事件有关的元素除外(通常包含在NotifyCollectionChangedEventArgs中) 现在我的想法是: 我们知道,任何实现INotifyCollectionChanged的东西都是一个集合(d'uh) 由于NotifyPropertyChangedEventArgs

我曾多次遇到这样的情况:我希望通过
INotifyCollectionChanged
界面观察一个集合,但也希望能够访问该集合的任何元素。
INotifyCollectionChanged
接口不提供任何访问元素的方法,但与更改事件有关的元素除外(通常包含在
NotifyCollectionChangedEventArgs
中)

现在我的想法是:

  • 我们知道,任何实现
    INotifyCollectionChanged
    的东西都是一个集合(d'uh)
  • 由于NotifyPropertyChangedEventArgs包含指示更改位置的索引,我们知道可以通过索引访问元素
  • 可以通过索引访问的集合是一个列表,因此要求任何
    INotifyCollectionChanged
    实现者也实现
    IList
    似乎是有意义的。这可以通过让
    INotifyCollectionChanged
    extend
    IList
    轻松实现


    有人知道为什么不是这样吗?

    我认为您需要查阅软件设计原则,特别是

    您询问为什么
    INotifyCollectionChanged
    接口不扩展
    IList
    接口。让我用Liskov替代原理回答一个反问题:

    我可以说一个
    INotifyCollectionChanged
    是一个
    IList

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

  • INotifyCollectionChanged
    表示,实现此接口的类需要在其基础集合发生更改时通知其用户,无论该基础集合是
    IList
    还是
    ICollection
    ,甚至是
    IEnumerable
    ,我们都不知道。这是一个不同的
    IList
    接口概念,它只是一个
    ICollection
    ,具有一个公开的

  • 您提到的
    NotifyPropertyChangedEventArgs
    (我相信您的意思是
    NotifyCollectionChangedEventArgs
    )公开了索引的属性,指示集合在什么位置被更改。但是,这并不意味着这些属性必须通过
    IList
    的索引器公开项目。它可以是一个任意的数字,一个神奇的常数,随便什么。由实现类决定如何公开索引

  • 为了演示这一点,请查看我的自定义类,它实现了
    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);
            }
        }
    }
    
    公共类MyCustomCollection:INotifyCollectionChanged
    {
    //这就是我所说的“基础集合”,可以用
    //ICollection,它仍然可以工作,甚至IEnumerable,但有一些
    //将元素存储在数组中的代码更改
    私有只读IList;
    公共MyCustomCollection()
    {
    _ints=新列表();
    }
    公共事件通知CollectionChangedEventHandler CollectionChanged;
    公共无效附加项(int i)
    {
    _内加(i);
    OnCollectionChanged(新建NotifyCollectionChangedEventArgs(
    NotifyCollectionChangedAction.Move,
    (IList),
    _积分计数,
    _整数计数-1);
    }
    CollectionChanged上受保护的虚拟空间(NotifyCollectionChangedEventArgs e)
    {
    var handler=CollectionChanged;
    if(处理程序!=null)
    {
    处理者(本,e);
    }
    }
    }
    

    希望这能回答你的问题。

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

  • 以这种方式实现
    INotifyCollectionChanged
    遵循角色接口的原则(更改通知是描述的而不是对象),请参见@rexcfnghk post

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


  • 总之,他们决定使用一个允许更改通知的界面将默认集合设置为一个
    集合。

    对我来说,你的答案基本上归结为“仅仅因为EventArgs中有索引并不意味着你可以实际使用它们”。那么为什么索引会出现呢?@Steven,你关注的是我回答的第二点,这不是我的意图:
    INotifyCollectionChanged
    没有扩展
    IList
    的主要原因是“
    INotifyCollectionChanged
    不是
    IList
    ”。我的第二点简单地说明了索引属性是一种抽象,而不是一种机制
    NotifyCollectionChangedEventArgs
    索引并不总是设置好的,例如,对于不是IList的ICollection。请参阅Stephen Cleary的。
    集合
    实现了
    IList
    ,因此该参数无效。如果集合不必是列表,那么我们应该如何使用EventArgs中给出的索引?@Steven,如果基础集合不是
    IList
    ,则可以使用简单的
    for
    循环来计算索引。