我遇到一个案例,其中List.AsReadOnly()
返回ReadOnlyCollection
而不是IReadOnlyCollection
的事实让我很难。由于返回的集合是Value
的{{1}},因此无法自动向上转发为Dictionary
。这看起来很奇怪,在查看.Net源代码后,我确认IReadOnlyCollection
方法对AsReadOnly()
的处理方式与对List
的处理方式不同,即返回具体类而不是界面。
任何人都能解释为什么会这样吗?这种不一致似乎是一种损害,特别是因为我们希望尽可能使用接口,特别是在公开时。
在我的代码中,我首先想到的是,由于我的消费者只是一个私有方法,我可以将其参数签名从Dictionary
更改为IReadOnlyDictionary<T, IReadOnlyCollection<T>>
。但是,我意识到这使得它看起来像私有方法可能会修改集合值,所以我在前面的代码中添加了一个恼人的显式强制转换,以便正确使用该接口:
IReadOnlyDictionary<T, ReadOnlyCollection<T>>
哦,因为我总是把协方差和矛盾变得困惑,有人可以告诉我哪一个阻止自动演员,并试着以明智的方式提醒我如何记住他们的未来? (例如,_____ [输入/输出]参数的集合不是______variant [co / contra]。)我理解为什么这不可能,因为可能有很多接口的实现,并且不能安全地投射所有字典的各个元素到请求的类型。除非我吹即使这个简单的方面,我不理解它,在这种情况下,我希望你可以帮助我正确...
答案 0 :(得分:0)
原因是历史性的。 {4.5}中添加了IReadOnly
*接口,而在.NET 2.0中添加了List<T>.AsReadOnly()
。改变其回报类型将是一个重大改变。
明确的演员表并不是那么糟糕。它甚至不是运行时强制转换,因为编译器可以静态验证它(没有向IL发送强制转换)。顺便说一句,您可以将其强制转换为IReadOnlyList<T>
,它还提供对列表的索引访问。您还可以编写一个返回所需类型的扩展方法(例如AsReadOnlyList()
)。
关于{co,contra}方差,我发现使用C#关键字in
(逆变)和out
(协变)更容易记住。 in
类型参数只能显示为输入方法参数,而out
类型参数只能显示为输出(返回值)。一种接受参数的方法,例如使用Base
类型调用Derived
类型是安全的,因此在该方向上转换in
参数是安全的。 out
正好相反。
例如:
interface IIn<in T> { Set(T value); }
IIn<Base> b = ...
IIn<Derived> d = b;
d.Set(derived); // safe since any method accepting Base can handle Derived
interface IOut<out T> { T Get(); }
IOut<Derived> d = ...
IOut<Base> b = d;
b.Get(); // safe since any Derived is Base
非只读集合接口不能成为*变体,因为它们必须同时是in
和out
,这样会不安全。编译器和CLR不允许这样做。 .NET确实与数组有一种不安全的差异形式:
var a = new[] { "s" };
var o = (object[])a;
o[0] = 1; // ArrayTypeMismatchException
您可以看到他们希望如何通过通用差异避免混乱。据推测,他们可以添加允许in
(cotravariant)方向的只写接口,但我猜他们并没有在那里找到很多价值。