C#3.5协变问题?

我一直听到/阅读了很多关于C#中的协变问题的文章,我想提出一些问题和场景,希望我能够澄清我对这个问题的困惑。

在这些例子中,请假定总是定义如下:

public class Apple : Fruit {}

我的第一个例子:

IList<Apple> apples = GetApples();
IList<Fruit> fruits = apples;

这应该工作,对吗? 我在C#中测试了几次,它编译得很好,运行正常(我的第一个示例的测试稍微多于这个,因为我使用多态调用将东西打印到控制台)。

第二个例子:

IList<Apple> apples = GetApples();
IList<object> fruits = apples;

在第二个示例中,我的理解是,这不应该编译,并且是.NET 4.0中解决的协方差问题的根源。 如果我错了,请纠正我。 我也意识到,.NET 4.0不允许具体类型,只有接口之间的协变/逆变。

最后,我想获得一些定义。 我不太清楚这三个术语背后的含义:

  • 协方差
  • 逆变
  • 不变性(与不变性相同)?
  • 至于最后一个字,我在C ++中用了很多来指代那些隐含给他们的规则的更改。 例如,如果我有一个整数,并且只允许有一个介于1和10之间的值,那么“不变性”就是它只能在1和10之间。我可能会误解这个,我也不确定是否这个定义很好地转换为C#来进行这个特定的讨论。

    编辑

    我的目标是准确理解C#中通用接口的协变或转换问题。 我发布的例子是我对问题出在哪里的理解。 如果所有示例都编译/运行良好,请提供一个例子,它重现了C#中最常见的协方差/变换/转换问题。 我需要知道这一点,以便我可以识别并向其他人解释问题。


    IList<T>接口没有被定义为协变的,因为它支持使对象变异的Add方法。

    考虑以下:

    IList<Apple> apples = GetApples();
    IList<object> fruits = apples;
    fruits.Add(new Banana());
    

    你现在可以从apples获得一个Banana ,这当然不是想要的。 因此, IList接口不支持协变(并且永远不会),并且应该会导致编译错误。

    你应该有同样的问题

    IList<Apple> apples = GetApples();
    IList<Fruit> fruits = apples;
    fruits.Add(new Banana());
    

    所以我不确定它为什么为你编译。

    IEnumerable<out T>接口可以是协变的(并且在.NET 4.0及更高版本中),因为IEnumerable只支持来自集合的读取元素。


    Scala语言具有类似的协变和逆变对象的概念,Scala编程中讨论泛型的章节也应该作为C#协变的一个很好的介绍。


    看看这篇文章中的协方差和逆变的解释。

    http://msdn.microsoft.com/en-us/library/dd799517.aspx


    CLR已经在泛型类型方面有了一些支持,并且使用c#4语法来使用它。 使用通用方差时,方差应用于接口和委托类型的类型参数。

    协变是关于能够将返回的值视为更一般的类型,并且当接口方法只返回该类型时可能。 在这个例子中,派生的接口实例可以被重新分配为基地,但不是其他方式。

    public interface ISomeInterfaceWithOut<out T>
    {
        T GetSomething();
    }
    
    ISomeInterfaceWithOut<Apple> b = new Blah<Apple>();
    ISomeInterfaceWithOut<Fruit> fruit = b;
    

    反变换是关于能够将参数类型视为更具体的类型,并且当接口方法仅使用该类型时可能是有可能的。 在这个例子中,基本接口实例可以被重新分配为派生的,但不是其他方式。

    public interface ISomeInterfaceWithIn<in T>
    {
        void SetSomething(T instance);
    }
    
    ISomeInterfaceWithIn<Fruit> b = new Blah<Fruit>();
    ISomeInterfaceWithIn<Apple> apple = b;
    

    不变性是两种情况都发生时,接口方法都返回并消耗类型。 协变或逆变都不适用。 这里任何类似上述的用法都不起作用,因为'out T'协方差或'in T'反变量类型参数不允许被定义为方法都包含这两种情况。

    考虑这个:

    //it is not possible to declare 'out T' or 'in T' here - invalid variance
    public interface ISomeInterface<T>
    {
        T GetSomething();
        void SetSomething(T instance);
    }
    

    你的两个例子都不会像现在这样工作。 逆变换/协方差适用于已将其泛型声明为“in”/“out”的接口和委托,IList是不变的。

    由于IEnumerable<T>接口与.NET 4是协变的,因此您可以从4开始但不是3.5开始。 这里使用水果作为IList在申报水果时不起作用 - 它不是协变的。

    List<Apple> apples = new List<Apple>();
    //List<Apple> apples implements IEnumerable<Apple>
    IEnumerable<Fruit> fruits = apples;
    

    这里是IEnumerable<T>的定义

    //Version=4.0.0.0
    public interface IEnumerable<out T> : IEnumerable
    {
        new IEnumerator<T> GetEnumerator();
    }
    
    //Version=2.0.0.0
    public interface IEnumerable<T> : IEnumerable
    {
        new IEnumerator<T> GetEnumerator();
    }
    
    链接地址: http://www.djcxy.com/p/85055.html

    上一篇: C# 3.5 Covariance issue?

    下一篇: Compile JasperReports with Gradle gets unsupported version error