C# 为什么';在.NET 4.5中,通用ICollection是否实现DirectAdonlyCollection?

C# 为什么';在.NET 4.5中,通用ICollection是否实现DirectAdonlyCollection?,c#,oop,design-patterns,.net-4.5,c#-5.0,C#,Oop,Design Patterns,.net 4.5,C# 5.0,在.NET 4.5/C#5中,IReadOnlyCollection使用Count属性声明: public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable { int Count { get; } } 公共接口IReadOnlyCollection:IEnumerable,IEnumerable { 整数计数{get;} } 我想知道,ICollection是否也可以实现IR

在.NET 4.5/C#5中,
IReadOnlyCollection
使用
Count
属性声明:

public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
{
    int Count { get; }
}
公共接口IReadOnlyCollection:IEnumerable,IEnumerable
{
整数计数{get;}
}
我想知道,
ICollection
是否也可以实现
IReadOnlyCollection
接口:

public interface ICollection<T> : IEnumerable<T>, IEnumerable, *IReadOnlyCollection<T>*
公共接口ICollection:IEnumerable,IEnumerable,*IReadOnlyCollection*
这意味着实现
ICollection
的类将自动实现
IReadOnlyCollection
。这听起来很合理

ICollection
抽象可以看作是
IReadOnlyCollection
抽象的扩展。请注意,例如,
List
实现了
ICollection
IReadOnlyCollection

然而,它并不是这样设计的

我错过了什么?为什么会选择当前的实现


更新

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

  • 一个具体的类,如
    List
    实现了
    IReadOnlyCollection
    ICollection
是比以下更好的设计:

  • i收集
    直接实施
    IReadOnlyCollection

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

  • 为什么IList不实现IReadOnlyList
  • 为什么
    IDictionary
    不实现
    IReadOnlyDictionary

  • 当我第一次读到你的问题时,我想“嘿,他是对的!那将是最有意义的。”但是界面的重点是描述一个合同——一个行为的描述。如果类实现了
    IReadOnlyCollection
    ,则它应该是只读的<代码>集合不是。因此,对于
    集合
    来说,实现一个隐含只读的接口可能会导致一些非常严重的问题。
    IReadOnlyCollection
    的消费者将对该基础集合做出某些假设,比如在它上面进行迭代是安全的,因为没有人可以修改它,等等。如果它是按照您建议的结构构建的,那么这可能不是真的

    可能有几个原因。以下是一些:

    • 巨大的向后兼容性问题

      您将如何编写
      i收集
      的定义?这看起来很自然:

      interface ICollection<T> : IReadOnlyCollection<T>
      {
          int Count { get; }
      }
      
      好的,这将打破所有决定显式实现
      Count
      的现有代码:

      class UnluckyClass : ICollection<Foo>
      {
           int ICollection<Foo>.Count { ... } // compiler error!
      }
      
      class解锁class:ICollection
      {
      int ICollection.Count{…}//编译器错误!
      }
      
      因此,在我看来,这个问题没有好的解决方案:要么破坏现有的代码,要么对每个人强制执行容易出错的实现。所以

      • 这在语义上是错误的,因为显然,不是每个
        i集合都是只读的

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


        然而,他们没有走那条路。为什么?我看到了一个。(坦率地说,尽管它已经是了。)

        这个接口更多的是一个描述接口,而不是真正的功能性接口

        在建议的方法中,每一个收藏都必须被理解为你只能阅读的东西,因为广告总是一样的。因此,您无法在创建后添加或删除对象

        我们可以问问自己,为什么该接口被称为
        IReadOnlyCollection
        ,而不是使用
        IEnumerable,IEnumerable
        的“ireadonlynumerable”。答案很简单,这种类型的接口没有任何意义,因为Enumerable已经是“只读的”

        那么让我们看看医生

        描述中的第一个(也是最后一个)意思是:

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

        我认为这可以解释一切,如果你知道它的用途的话。

        面向对象的设计推理将源于类和类实现的任何接口之间的“是一个”关系

        在.NET 4.5/c中,5.0
        List
        既是
        i集合
        又是
        IReadOnlyCollection
        。您可以将
        列表
        实例化为任意一种类型,具体取决于您希望集合的使用方式

        当使用
        ICollection
        实现
        IReadOnlyCollection
        时,您可以将
        List
        设置为
        ICollection
        IReadOnlyCollection
        ,这样做就意味着
        ICollection
        “是一个”
        IReadOnlyCollection
        ,而不是

        已更新

        如果从
        ICollection
        继承的每个类都实现了
        IReadOnlyCollection
        ,那么我认为让
        ICollection
        实现接口更有意义。由于情况并非如此,请考虑如果
        ICollection
        要实现
        IReadOnlyCollection
        ,将会是什么样子:

        
        公共接口IReadOnlyCollection:IEnumerable,IEnumerable{}
        公共接口ICollection:IReadOnlyCollection{}

        如果要查看
        ICollection
        的签名,它看起来像是一个只读集合。等等,让我们看看IReadOnlyCollection和啊…它实现了
        IEnumerable
        IEnumerable
        ,所以它不一定是只读集合。从功能上讲,原始问题提出的实现是相同的,但从语义上讲,我认为将接口实现推到尽可能高的位置更为正确。

        Jon就在这里,您应该将他的回答标记为
        class UnluckyClass : ICollection<Foo>
        {
             int ICollection<Foo>.Count { ... } // compiler error!
        }
        
        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) { }
        }