.NET 4.0 Covariance

本文关键字:Covariance NET | 更新日期: 2023-09-27 18:25:23

为了回答另一个问题,我尝试执行以下操作。 我不认为我正确解释了这个问题,但我确实想知道以下情况是否可能以某种方式(我的尝试失败了(,如果不是,为什么不呢:

    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(的想法(是不可能的,不受欢迎的,还是在"也许有一天"的清单上?

.NET 4.0 Covariance

设计决策不对泛型类类型实现方差背后的想法是什么?这是不可能的,不受欢迎的还是在"也许有一天"的清单上?

你是在好公司;几周前,乔恩·斯基特(Jon Skeet(和比尔·瓦格纳(Bill Wagner(问了我同样的问题。我一直在写一篇关于它的博客文章,但简短地说:

寻求明确答案的合适人选是剑桥Microsoft研究中心的安德鲁·肯尼迪(Andrew Kennedy(,他最初设计并实现了大部分通用和方差逻辑。但是,我可以冒昧地猜测为什么我们决定避免泛型类的差异。

简短的版本是:T 的安全协方差要求对 T 的操作是"只读的"。T 的逆变要求对 T 的操作是"只写"。C<T>假设 'C 有一个类型 T 的字段:您希望该字段仅可读还是仅可写?因为这些是你的选择!

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

简而言之,逆变泛型类

几乎从来没用,因为您无法从中读取任何泛型数据,而协变类大多仅在类是不可变数据类型时才有用

我是不可变

数据类型的忠实粉丝,我认为能够在不涉及接口的情况下制作协变不可变堆栈将是一个很棒的功能。但是协变泛型持久不可变函数数据结构在 C# 中还不是主流,当泛型添加到 CLR 时,它们肯定不是主流。此外,除了只读字段之外,我们没有支持基础结构来表达 C# 或 CLR 中的"这是一种不可变的数据类型"的概念;如果我们要为不可变类做协变类类型,那么做很多支持不可变类的功能会很好,而不仅仅是这个晦涩难懂的类。

所以我可以看到这个功能在 CLR 2.0/C# 2.0 中没有被削减。如果我们今天再次设计它,当函数式编程更受欢迎时,也许它会。但我们没有计划在短期内这样做。

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

接口是协变的;类不是。

A<MyClass>可以转换为 IB<MyBaseClass> .

直接类到类的转换不起作用,但是如果您将IB接口用于变量类型,则可以使用。 根据此 MSDN 协方差常见问题解答博客文章和有关协方差和逆变的 MSDN 页面,变体类型参数仅限于泛型接口和泛型委托类型。

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>();
}