.NET 4.0协方差

在回答另一个问题时,我试图做到以下几点。 我不认为我正确地解释了这个问题,但我确实想知道下面是否有可能(我的尝试失败了),如果不是为什么不:

public class MyBaseClass { } public class MyClass : MyBaseClass { } public class B { } public class A : B { } static void Main(string[] args) { // Does not compile B myVar = new A(); } 

我认为这可以使用带有协变类型参数的通用接口来工作:

  interface IB { } public class B : IB { } 

但我错了,那也行不通。

编辑

正如SLaks指出的那样’接口是协变的; 上课不是。’ (感谢SLaks)。 所以现在我的问题是为什么? 设计背后的想法(我认为Eric Lippert的一个想法)是不可能的,不受欢迎的还是在“可能一天”的名单上?

设计决策背后的想法是什么,不在generics类类型上实现差异? 是不可能的,不受欢迎的还是在“可能一天”的名单上?

你们在一起很好; 几周前Jon Skeet和Bill Wagner问我同样的问题。 我一直在撰写关于它的博客文章,但简要地说:

要求最终答案的合适人选是Microsoft Research Cambridge的Andrew Kennedy,他最初设计并实现了许多通用和差异逻辑。 但是,我可以冒险猜测为什么我们决定避免generics类的差异。

简短版本是:T的安全协方差要求T上的操作是“只读”。 T的逆变量要求对T的操作是“只写”。 你有一个C类,你希望在T中变化。假设`C有一个T类型的字段:你想让那个字段只能读只能写吗? 因为那些是你的选择!

在什么情况下,拥有一个可以写入但不能读取的字段甚至是模糊的有用? 不多。 在什么情况下有一个可以读取但不能写入的字段是有用的? 仅当字段标记为只读时

简而言之,逆变generics类几乎从不有用,因为您无法从中读取任何通用数据,并且协变类通常仅在类是不可变数据类型时才有用

我是不可变数据类型的忠实粉丝,我认为能够在不必涉及接口的情况下创建协变不可变堆栈将是一个很棒的function。 但协变通用持久不可变function数据结构在C#中并不完全是主流,并且当将generics添加到CLR时它们肯定不是。 此外,除了只读字段之外,我们没有在C#或CLR中表达“这是一种不可变数据类型”的概念; 如果我们要为不可变类做covariant类类型,那么做很多支持不可变类的特性会很好,而不仅仅是这个不起眼的类。

所以我可以看到这个function如何不能在CLR 2.0 / C#2.0中实现。 如果我们今天再次设计它,当function风格的编程更受欢迎时,也许它会。 但我们没有计划很快这样做。

我会在接下来的几个月写一篇博文,给出更详细的答案。

接口是协变的; 课不是。

A可以转换为IB

直接类到类的转换不起作用,但是如果你使用IB接口作为变量类型,你就可以了。 根据此MSDN协方差常见问题解答博客文章和协方差和反演的MSDN页面 ,变体类型参数仅限于通用接口和通用委托类型。

 public class MyBaseClass {} public class MyClass : MyBaseClass {} interface IB{} public class B : IB {} public class A : B {} static void Main(string[] args) { IB myVar = new A(); }