为了回应另一个question,我试图做以下事情。我不认为我正确地解释了这个问题,但我确实想知道下面是否有可能(我的尝试失败了),如果不是为什么不:
public class MyBaseClass { }
public class MyClass : MyBaseClass { }
public class B<T> { }
public class A<T> : B<T> { }
static void Main(string[] args)
{
// Does not compile
B<MyBaseClass> myVar = new A<MyClass>();
}
我认为这可以使用具有协变类型参数的通用接口来工作:
interface IB<out T> { }
public class B<T> : IB<T> { }
但我错了,这也行不通。
编辑
正如SLaks指出的那样'接口是协变的;上课不是。' (感谢SLaks)。所以现在我的问题是为什么?这个设计背后的想法(我认为Eric Lippert的一个想法)是不可能的,不受欢迎的还是在“可能一天”的名单上?
设计决策背后的想法是什么,不在泛型类类型上实现差异?是不可能的,不受欢迎的还是在“可能有一天”的名单上?
你们在一起很好;几周前Jon Skeet和Bill Wagner问我同样的问题。我一直在撰写关于它的博客文章,但简要地说:
要求获得明确答案的合适人选是微软研究院剑桥大学的安德鲁·肯尼迪,他最初设计并实现了许多通用和方差逻辑。但是,我可以冒险猜测为什么我们决定避免泛型类的差异。
简短版本是:T的安全协方差要求T上的操作是“只读”。 T的逆变量要求T上的运算是“只写”。你有一个类C<T>
,你希望在T中变化。假设`C有一个T类型的字段:你想让那个字段只能读或只能写吗?因为那些是你的选择!
在什么情况下,有一个可以写入但不能读取的字段甚至是模糊的有用?不多。在什么情况下有一个可以读取但不能写入的字段是有用的?仅当字段标记为只读时。
简而言之,逆变泛型类几乎从不有用,因为您无法从中读取任何通用数据,并且协变类通常仅在类是不可变数据类型时才有用。
我是不可变数据类型的忠实粉丝,我认为能够在不必涉及接口的情况下创建协变不可变堆栈将是一个很棒的功能。但协变通用持久不可变功能数据结构在C#中并不完全是主流,并且当将泛型添加到CLR时它们肯定不是。此外,除了readonly字段之外,我们没有任何支持基础结构来表达C#或CLR中的“这是一个不可变数据类型”的概念;如果我们要为不可变类做covariant类类型,那么做很多支持不可变类的特性会很好,而不仅仅是这个不起眼的类。
所以我可以看到这个功能是如何在CLR 2.0 / C#2.0中完成的。如果我们今天再次设计它,当功能风格的编程更受欢迎时,也许它会。但我们没有计划很快这样做。
我会在接下来的几个月写一篇博文,给出更详细的答案。
接口是协变的;课不是。
A<MyClass>
可以转换为IB<MyBaseClass>
。
直接类到类的转换不起作用,但是如果你使用IB接口作为变量类型,你就可以了。根据此MSDN covariance FAQ blog post和MSDN page on Covariance and Contravariance,变体类型参数仅限于通用接口和通用委托类型。
public class MyBaseClass {}
public class MyClass : MyBaseClass {}
interface IB<out T>{}
public class B<T> : IB<T> {}
public class A<T> : B<T> {}
static void Main(string[] args)
{
IB<MyBaseClass> myVar = new A<MyClass>();
}