我曾经遇到过几次我希望通过INotifyCollectionChanged
界面观察集合的情况,但也希望能够访问任何集合的元素。 INotifyCollectionChanged
接口不提供任何访问元素的方法,除了那些参与更改事件的元素(通常)包含在NotifyCollectionChangedEventArgs
中)。
现在我的想法是:
INotifyCollectionChanged
都是一个集合(呃)。NotifyPropertyChangedEventArgs
包含指示更改位置的索引,因此我们知道可以通过索引访问这些元素。可以通过索引访问的集合是一个列表,因此似乎要求任何INotifyCollectionChanged
实现者也实现IList
是有意义的。这可以通过INotifyCollectionChanged
扩展IList
来轻松完成。
有谁知道为什么不是这种情况?
答案 0 :(得分:3)
我认为您需要查找SOLID软件设计原则,特别是Liskov Substitution Principle。
您问为什么INotifyCollectionChanged
接口不会扩展IList
接口。让我用Liskov Subsitution Principle的反问题来回答:
我可以说
INotifyCollectionChanged
是IList
吗?
不,我不这么认为,原因如下:
INotifyCollectionChanged
表达了实现此接口的类需要通知其用户是否更改了其基础集合,该基础集合是IList
还是ICollection
,或者即使IEnumerable
,我们也不知道。这是IList
界面的不同概念,它只是ICollection
,具有公开的indexer
您提到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);
}
}
}
希望这能回答你的问题。
答案 1 :(得分:1)
我不知道为什么,但我看到了这个决定的原因(不知道决定 - 这只是我的意见):
以这种方式实现INotifyCollectionChanged
遵循角色接口的原则(描述更改通知而不是对象)请参阅@rexcfnghk帖子!
只有EventArgs
中更改的元素表示为IList<T>
,这并不意味着集合本身需要是列表
总而言之,他们决定使用允许更改通知的Collection<T>
来创建默认集合。