持久集合和标准集合接口

时间:2012-01-16 19:19:24

标签: c# .net collections

我实现了一个持久的集合 - 为了论证,让我们说它是一个单链表,是函数式语言中常见的样式。

class MyList<T>
{
    public T Head { get; }
    public MyList<T> Tail { get; }

    // other various stuff
    // . . .
}

这个类实现ICollection<T>似乎很自然,因为它可以实现人们对ICollection<T>所期望的所有正常行为,至少在广泛的行程中。但是这个类的行为和ICollection<T>之间存在很多不匹配。例如,Add()方法的签名

void Add(T item);  // ICollection<T> version

假设添加将作为改变集合的副作用执行。但这是一个持久的数据结构,因此Add()应该创建一个新列表并返回它。

MyList<T> Add(T item);  // what we really want

解决此问题的最佳方法似乎是创建我们想要的版本,并生成界面中定义的版本的非功能性显式实现。

void ICollection<T>.Add(T item) { throw new NotSupportedException(); }
public MyList<T> Add(T item) { return new MyList<T>(item, this); }

但我对这个选项有一些顾虑:

  1. 这会让用户感到困惑吗?我设想有人正在使用这个类的场景,并发现在它上面调用Add()有时会引发异常,有时会运行但不会像ICollection一样预期修改列表,具体取决于类型与正在使用的参考相关的信息?

  2. 继续(1)之后,ICollection<T> IsReadOnly的实施应该可以归结为真。但这似乎与Add()与该类实例一起使用的其他地方隐含的内容相冲突。

  3. 是否(2)通过再次遵循显式实现模式以非混淆的方式解决,新版本返回false并且显式实现返回true?或者这只是因为错误地暗示MyList<T>的{​​{1}}方法是一个改变者而使情况变得更糟?

  4. 或者最好忘记尝试使用现有界面,只创建一个源自Add()的单独IPersistentCollection<T>界面?

  5. 编辑我更改了该类的名称,并切换到使用ICollection。我想专注于对象的行为以及它与界面的关系。我只是将cons清单作为一个简单的例子。我很欣赏这样的建议:如果我要实现一个缺点列表,我应该尝试提出一个较少混淆的名称,并且应该避免实现IList,因为该接口用于快速随机访问,但它们有些切线问题。

    我打算询问的是其他人对于烘焙到框架中的只读(或不可变)集合的语义与实现与接口所描述的内容等效行为的持久集合之间的紧张关系,只是在功能上,而不是通过突变副作用。

2 个答案:

答案 0 :(得分:5)

  

实施IList<T>会让人感到困惑吗?

是。虽然有些情况下IList<T>的实现会抛出 - 例如,当您尝试调整列表的大小但是它的实现是一个数组时 - 我会发现有一个IList<T>令人困惑可以突然变异并且没有快速随机访问。

  

我应该实施新的IPersistentList<T>吗?

这取决于是否有人会使用它。您班级的消费者是否可能有六种不同的IPL<T>实施可供选择?我认为制作一个只由一个类实现的接口没有意义;只需使用该课程。

  

如果其ItemsSource为IList<T>而不是IEnumerable<T>,则WPF的ItemsControl可以获得更好的性能。

但是你的持久性链表无论如何都不会有快速随机访问。

答案 1 :(得分:3)

对我来说更有意义的做法是向我发出一个新的IPersistentList<T>(或IImmutableList<T>自“持久”声音,就像数据被保存在某个地方一样。)接口因为,真的,它是不同的行为比IList<T>的预期更多。实现IList<T>的类应该是可变的恕我直言。

哦,当然,我会避免使用类名List<T>,因为它已经是框架的一部分。