Jas*_*dge 9 c# type-systems .net-4.5
我一直在阅读.NET4.5将带来的变化,在这篇博客文章中,我偶然发现了一些我既不知道也不理解的东西.
在谈到只读集合的实现时,Immo Landwerth说:
不幸的是,我们的类型系统不允许制作T协变类型,除非它没有将T作为输入的方法.因此,我们无法向IReadOnlyList添加IndexOf方法.我们认为,与不支持协方差相比,这是一个小小的牺牲.
从我明显有限的理解,似乎他说,为了使我们能够调用一个需要IReadOnlyList<Shape>通过传入IReadOnlyList<Circle>的IReadOnlyList<T>.IndexOf(T someShape)方法,我们不能有一个方法.
我没有看到类型系统如何阻止它.谁能解释一下?
Jon*_*eet 11
假设Circle工具IEquatable<Circle>.IReadOnlyList<Circle>.IndexOf如果可以的话,这自然会被使用.现在,如果你能写下这个:
IReadOnlyList<Circle> circles = ...;
IReadOnlyList<Shape> shapes = circles;
int index = shapes.IndexOf(new Square(10));
Run Code Online (Sandbox Code Playgroud)
这最终会试图通过一个Square到Circle.Equals(Circle)这显然是一个坏主意.
强制执行" T输入位置无值"的规则在C#4规范的第13.1.3节中.你也应该阅读埃里克利珀对通用方差博客系列的很多详细信息.
由于IReadOnlyList<T>是协变的,你可以将其转换为任何超类型,T并且所有方法仍然应该根据合同工作.最超级的超类型T是Object,所以如果IndexOf是界面的一部分,它应该已被接受Object.
正如Jon Skeet所说,在一个Circle对象列表中,您可以问:Square此列表中特定内容的索引是什么?唯一正确的响应是"它不在这里",并且IndexOf应该返回-1而不是抛出异常.
所以,我不同意Jon Skeet.鉴于协变通用参数的局限性,以及类似于ArrayList.indexOfJava,BCL团队应该包含一个IndexOf具有以下签名的方法:
int IndexOf(object item);
Run Code Online (Sandbox Code Playgroud)
完全相同的参数适用Contains于IReadOnlyCollection:当您传入一个不兼容类型的对象时,该集合显然不包含它,并且该方法应该只返回false.
唯一的缺点是拳击值类型,但实际的实现仍然可以隐藏IReadOnlyList.IndexOf方法并提供自己的泛型重载,使这个论点没有实际意义.
因此,IndexOf在传递不兼容的对象(如果它在接口上)时,您希望返回-1 是正确的.
我在我的M42.Collections库中实现了这个原则,以说明这在实践中是如何工作的.您可以在这里下载:
M42 Collections - 用于正确处理集合的可移植.NET库.