【问题标题】:.NET 4.0 Covariance.NET 4.0 协方差
【发布时间】:2012-02-21 16:45:40
【问题描述】:

为了回应另一个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 的想法)是不可能的、不可取的还是在“也许有一天”的清单上?

【问题讨论】:

    标签: c# .net generics covariance


    【解决方案1】:

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

    你们相处得很好; Jon Skeet 和 Bill Wagner 几周前问了我同样的问题。我一直在写一篇关于它的博客文章,但很简短:

    微软剑桥研究院的 Andrew Kennedy 是寻求明确答案的合适人选,他最初设计并实现了大部分通用逻辑和方差逻辑。不过,我可以大胆猜测一下为什么我们决定避免泛型类的差异。

    简短的版本是:T 的安全协方差要求对 T 的操作是“只读的”。 T 的逆变要求对 T 的操作是“只写”的。你有一个类C&lt;T&gt;,你希望它在 T 中变体。假设`C 有一个类型为 T 的字段:你想让该字段只读还是只写?因为这些都是你的选择!

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

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

    我是不可变数据类型的忠实拥护者,我认为能够在不涉及接口的情况下创建协变不可变堆栈将是一个很棒的功能。但是协变泛型持久不可变功能数据结构在 C# 中还不是完全主流,当泛型添加到 CLR 时,它们肯定不是主流。此外,除了只读字段之外,我们没有其他支持基础设施来表达 C# 或 CLR 中“这是一种不可变数据类型”的概念;如果我们要为不可变类做协变类类型,最好做很多支持不可变类的特性,而不仅仅是这个晦涩难懂的特性。

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

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

    【讨论】:

    • 我认为,如果一个泛型类由一个或多个T 消费者初始化,它可以向其提供T 类型的任何项目,那么它可能与T 具有非平凡的逆变性。被给予。关于不可变类型,我希望看到的是在可变对象和不可变对象之间平滑移动数据的框架/模式——在仅使用可变对象时所需的防御性复制之间的折衷,生成许多短-使用不可变对象时的活实例,以及“纯”写时复制的代价高昂的线程交互。
    • 我的 2 美分。这里的权衡之一是要么有一个简单明了的规则,即“没有变体类”,要么支持更多的场景,有更复杂的规则和不明显的警告。嗯,这两种方法各有利弊。
    • 有趣。请问可以给我博文的链接吗?
    【解决方案2】:

    接口是协变的;类不是。

    A&lt;MyClass&gt; 可以转换为IB&lt;MyBaseClass&gt;

    【讨论】:

      【解决方案3】:

      类到类的直接转换不起作用,但如果您将 IB 接口用于变量类型,那么您就可以使用了。根据 MSDN covariance FAQ blog postMSDN 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>();
      }
      

      【讨论】:

      • 谢谢 Rich,但我们已经从 SLaks 的答案中得到了这个答案,两次发布相同的答案通常是不好的形式。
      • @MylesMcDonnell 同意,但他确实提供了更多详细信息和链接,所以我认为它确实增加了讨论...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多