为什么通用ICollection在.NET 4.5中实现IReadOnlyCollection?
在.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>
。
然而它并没有被devise成这样。
我在这里错过了什么? 为什么会select当前的实施呢?
UPDATE
我正在寻找一个使用面向对象devise推理来解释原因的答案:
- 实现
IReadOnlyCollection<T>
和ICollection<T>
具体类如List<T>
ICollection<T>
是比以下更好的devise:
-
ICollection<T>
IReadOnlyCollection<T>
直接实现IReadOnlyCollection<T>
还请注意,这基本上是一样的问题:
- 为什么
IList<T>
实现IReadOnlyList<T>
? - 为什么不
IDictionary<T>
实现IReadOnlyDictionary<T>
?
乔恩在这里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>
…但不是他们的接口。
恕我直言,他们做了一个devise错误最初,现在相当无法解决(没有打破东西)。
编辑:隐藏不帮助,旧的(显式的)实现将不会仍然生成(不修改代码):
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()'
可能有几个原因。 这里有两个顶部:
-
巨大的向后兼容性问题
你将如何写
ICollection<T>
的定义? 这看起来很自然:interface ICollection<T> : IReadOnlyCollection<T> { int Count { get; } }
但它有一个问题,因为
IReadOnlyCollection<T>
也声明了一个Count
属性(编译器会在这里发出警告)。 除了这个警告之外,保持原样(相当于编写new int Count
)允许实现者通过至less一个显式地实现两个Count
属性的不同实现。 如果两个实现决定返回不同的值,这可能是“有趣的”。 允许人们用脚踢自己而不是C#的风格。好的,那么怎么样:
interface ICollection<T> : IReadOnlyCollection<T> { // Count is "inherited" from IReadOnlyCollection<T> }
那么,这将打破所有决定明确实现
Count
现有代码:class UnluckyClass : ICollection<Foo> { int ICollection<Foo>.Count { ... } // compiler error! }
因此,在我看来,这个问题没有好的解决scheme:要么破坏现有的代码,要么迫使每个人都容易出错。 所以唯一的胜利就是不玩 。
-
语义运行时types检查
编写像这样的代码将是没有意义的
if (collection is IReadOnlyCollection<Foo>)
(或reflection的等价物),除非
IReadOnlyCollection<T>
是“opt-in”:如果ICollection<T>
是IReadOnlyCollection<Foo>
的超集,那么太阳下的每个集合类都会通过这个testing,在实践中。
这在语义上是错误的,因为显然不是每个ICollection
都是只读的。
也就是说,他们可以调用接口IReadableCollection
,而一个实现可以被称为ReadOnlyCollection
。
但是,他们没有走这条路。 为什么? 我看到一位BCL团队成员写道,他们不希望收集API变得过于复杂 。 (坦率地说,这已经是了。)
当我第一次读到你的问题时,我想:“嘿,他是对的!那会最有意义的。” 但是界面的要点是描述一个合同 – 对行为的描述。 如果你的类实现了IReadOnlyCollection
,它应该是只读的。 Collection<T>
不是。 所以对于一个Collection<T>
来实现一个表示只读的接口可能会导致一些非常讨厌的问题。 IReadOnlyCollection
消费者将会对这个底层集合做出某些假设 – 比如迭代它是安全的,因为没有人可以修改它等等。如果它的结构如你所说,可能不是真的!
面向对象的devise推理来源于类和类实现的任何接口之间的“是”关系。
在.NET 4.5 / c#5.0中, List<T>
既是ICollection<T>
又是IReadOnlyCollection<T>
。 您可以将List<T>
实例化为任一types,具体取决于您希望如何使用集合。
当ICollection<T>
实现IReadOnlyCollection<T>
,您可以让List<T>
成为ICollection<T>
或IReadOnlyCollection<T>
,这样做可以说ICollection<T>
“是A” IReadOnlyCollection<T>
它不是。
更新
如果从ICollection<T>
inheritance的每个类实现了IReadOnlyCollection<T>
那么我认为使ICollection<T>
实现接口更有意义。 由于情况并非如此,请考虑如果ICollection<T>
实现IReadOnlyCollection<T>
会是什么样子:
public interface IReadOnlyCollection<T> : IEnumerable<T>, IEnumerable{}
public interface ICollection<T> : IReadOnlyCollection<T> {}
如果您要查看ICollection<T>
的签名,它看起来像是一个只读集合。 等等,让我们看看IReadOnlyCollection和ahh …它实现IEnumerable<T>
和IEnumerable
所以它不一定是只读集合。 在function上,原始问题提出的实现是相同的,但从语义上讲,我认为将接口实现尽可能推高是更正确的。
这个接口更多的是一个描述接口,然后真正function的东西
在build议的方法中,每一个collections都必须被理解为你只能阅读广告的东西总是一样的。 所以你不能在创build之后添加或删除一个东西。
我们可以问自己为什么这个接口被称为IReadOnlyCollection
,而不是'IReadOnlyEnumerable',因为它使用IEnumerable<T>, IEnumerable
。 答案很简单,这种types的接口不会有任何的困难,因为Enumerable已经是“只读”了。
所以让我们看看文档IReadOnlyCollection(T)
描述中的第一个(也是最后一个)塞内加说:
表示一个强types的只读元素集合。
而且我认为这个解释了一切,如果你知道什么是强types的。
- 来自.Net 4.5的asynchronousHttpClient是密集加载应用程序的不好select吗?
- 无法首先将其转换为委托或expression式树types,不能将lambdaexpression式用作dynamic调度操作的参数
- 是否有可能在XP上运行.NET 4.5应用程序?
- 等待运营商只能在一个asynchronous方法内使用
- .NET中高级.NET 4.0和.NET 4.5之间的区别
- 从.Net 4.0到4.5的redirect解决scheme – 如何重新调整NuGet包?
- 为什么会find一个types的初始化器抛出一个NullReferenceException?
- HttpClient和HttpClientHandler必须被丢弃吗?
- 等待多个任务结果不同