为什么通用ICollection在.NET 4.5中实现IReadOnlyCollection?

时间:2012-09-27 13:28:23

标签: c# oop design-patterns .net-4.5 c#-5.0

在.NET 4.5 / C#5中,IReadOnlyCollection<T>声明为Count属性:

public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
{
    int Count { get; }
}

我想知道,ICollection<T>实现IReadOnlyCollection<T>接口是不是有意义:

public interface ICollection<T> : IEnumerable<T>, IEnumerable, *IReadOnlyCollection<T>*

这意味着实施ICollection<T>的类会自动实现IReadOnlyCollection<T>。这对我来说听起来很合理。

ICollection<T>抽象可以被视为IReadOnlyCollection<T>抽象的扩展。请注意,List<T>例如同时实现了ICollection<T>IReadOnlyCollection<T>

然而,它并没有这样设计。

我在这里缺少什么?为什么要选择当前的实现呢?


更新

我正在寻找一个使用面向对象的设计推理来解释原因的答案:

  • 具体实施List<T> IReadOnlyCollection<T>
  • ICollection<T>

是一个比以下更好的设计:

  • ICollection<T>直接实施IReadOnlyCollection<T>

另请注意,这与以下问题基本相同:

  1. 为什么不IList<T>实施IReadOnlyList<T>
  2. 为什么不IDictionary<T>实施IReadOnlyDictionary<T>

6 个答案:

答案 0 :(得分:35)

可能有几个原因。这里有两个顶部:

  1. 巨大的向后兼容性问题

    您如何撰写ICollection<T>的定义?这看起来很自然:

    interface ICollection<T> : IReadOnlyCollection<T>
    {
        int Count { get; }
    }
    

    但它有一个问题,因为IReadOnlyCollection<T>也声明了一个Count属性(编译器会在这里发出警告)。除了警告之外,将其保留为原样(相当于编写new int Count)允许实现者通过至少实现一个Count属性来实现不同的实现。如果两个实现决定返回不同的值,这可能是“有趣的”。允许人们用脚射击自己不是C#的风格。

    好的,那么:

    interface ICollection<T> : IReadOnlyCollection<T>
    {
        // Count is "inherited" from IReadOnlyCollection<T>
    }
    

    好吧,这会破坏所有决定明确实现Count的现有代码:

    class UnluckyClass : ICollection<Foo>
    {
         int ICollection<Foo>.Count { ... } // compiler error!
    }
    

    因此,在我看来,这个问题没有很好的解决方案:要么破坏现有代码,要么在所有人上强制执行容易出错的实现。所以the only winning move is not to play

  2. 语义运行时类型检查

    编写像

    这样的代码毫无意义
    if (collection is IReadOnlyCollection<Foo>)
    

    (或等效与反思),除非IReadOnlyCollection<T>是“选择加入”:如果ICollection<T>IReadOnlyCollection<Foo>的超集,则几乎所有太阳下的集合类都会通过此测试,使其在实践中无用。

答案 1 :(得分:14)

Jon就在这里https://stackoverflow.com/a/12622784/395144,你应该将答案标记为答案:

int ICollection<Foo>.Count { ... } // compiler error!

由于接口可以具有显式实现,因此提取基接口不向后兼容(对于基类,您没有此问题)。

这就是为什么......

Collection<T> : IReadOnlyCollection<T>
List<T> : IReadOnlyList<T>
Dictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>

......但不是他们的界面。

恕我直言,他们最初做了一个设计错误,现在完全无法解决(没有破坏事情)。

编辑:隐藏没有帮助,旧(显式)实现仍然不会构建(不修改代码):

interface INew<out T> { T Get(); }

interface IOld<T> : INew<T>
{
    void Set(T value);
    new T Get();
}

class Old<T> : IOld<T>
{
    T IOld<T>.Get() { return default(T); }
    void IOld<T>.Set(T value) { }
}
  

'Sample.Old'未实现接口成员'Sample.INew.Get()'

答案 2 :(得分:3)

语义错误,因为很明显,并非每个ICollection都是只读的。

也就是说,他们可以调用接口IReadableCollection,而实现可以调用ReadOnlyCollection

然而,他们并没有走那条路。为什么?我看到了BCL team member write that they didn't want the collections API to become too convoluted。 (虽然它已经是,但坦率地说。)

答案 3 :(得分:-2)

当我第一次阅读你的问题时,我想“嘿,他是对的!这才最有意义。”但接口的意义在于描述合同 - 行为的描述。如果您的类实现了IReadOnlyCollection,那么 应该是只读的。 Collection<T>不是。因此,对于Collection<T>实现隐含只读的接口可能会导致一些非常讨厌的问题。 IReadOnlyCollection的消费者会对底层集合做出某些假设 - 比如迭代它是安全的,因为没有人可以修改它等等。如果它的结构如你所暗示的那样可能不是真的!

答案 4 :(得分:-3)

面向对象的设计推理将源于类与该类实现的任何接口之间的“Is A”关系。

在.NET 4.5 / c#5.0 List<T>中,ICollection<T>IReadOnlyCollection<T>都是List<T>。您可以将ICollection<T>实例化为任一类型,具体取决于您希望如何使用集合。

虽然IReadOnlyCollection<T> List<T>实施ICollection<T>会让您IReadOnlyCollection<T>成为ICollection<T>IReadOnlyCollection<T>,但这样做会{ {1}} “是A” ICollection<T>它不是。

已更新

如果从IReadOnlyCollection<T>继承的每个类都实现了ICollection<T>,那么我会说让ICollection<T>实现接口更有意义。由于情况并非如此,请考虑IReadOnlyCollection<T>实施 public interface IReadOnlyCollection<T> : IEnumerable<T>, IEnumerable{}
public interface ICollection<T> : IReadOnlyCollection<T> {}
时的情况:

ICollection<T>

如果你要查看IEnumerable<T>的签名,它看起来像 IS-A 只读集合。等等,让我们看看IReadOnlyCollection和啊......它实现了IEnumerable和{{1}}所以它不一定是只读集合。在功能上,原始问题提出的实现是相同的,但从语义上来说,我认为将接口实现推到尽可能高的位置更为正确。

答案 5 :(得分:-4)

这个界面更像是一个描述界面然后真正的功能性的东西。

在建议的方法中,每个集合必须被理解为您可以完全阅读广告的内容始终相同。因此,您无法在创建后添加或删除任何内容。

我们可以问自己为什么界面被称为IReadOnlyCollection,而不是'IReadOnlyEnumerable',因为它使用IEnumerable<T>, IEnumerable。答案很简单,这种类型的界面不会有任何senece,因为Enumerable已经是“只读”。

因此,请查看文档IReadOnlyCollection(T)

描述中的第一个(也是最后一个)senetece说:

  

表示强类型的只读元素集合。

如果您知道strong-typing的用途,我认为这可以解释所有内容。