37

在下面的代码中,我希望能够从elementsto隐式转换,baseElements因为TBase它可以隐式转换为IBase.

public interface IBase { }
public interface IDerived : IBase { }
public class VarianceBug
{
    public void Foo<TBase>() where TBase : IBase
    {
        IEnumerable<TBase> elements = null;
        IEnumerable<IDerived> derivedElements = null;
        IEnumerable<IBase> baseElements;

        // works fine
        baseElements = derivedElements;

        // error CS0266: Cannot implicitly convert type 
        //   'System.Collections.Generic.IEnumerable<TBase>' to 
        //   'System.Collections.Generic.IEnumerable<IBase>'. 
        //   An explicit conversion exists (are you missing a cast?)
        baseElements = elements;
    }
}

但是,我收到评论中提到的错误。

引用规范:

如果是使用变体类型参数声明的接口或委托类型,并且对于每个变体类型参数,则类型可以变型T<A1, …, An>转换为类型,并且以下内容之一成立:T<B1, …, Bn>TT<X1, …, Xn>Xi

  • Xi是协变的,并且存在从Ai到的隐式引用或身份转换Bi

  • Xi是逆变的,并且存在从Bi到的隐式引用或身份转换Ai

  • Xi是不变的,并且存在从Ai到的身份转换Bi

检查我的代码,它似乎与规范一致:

  • IEnumerable<out T>是一个接口类型

  • IEnumerable<out T>用变体类型参数声明

  • T是协变的

  • TBase从到存在隐式引用转换IBase

那么 - 这是 C# 4 编译器中的错误吗?

4

2 回答 2

51

方差仅适用于引用类型(或存在身份转换)。不知道这TBase是引用类型,除非您添加: class

 public void Foo<TBase>() where TBase : class, IBase

因为我可以写一个:

public struct Evil : IBase {}
于 2010-05-06T18:16:23.470 回答
14

Marc 是正确的 - 我正要粘贴相同的回复。

请参阅协方差和逆变常见问题解答:

http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

从常见问题解答:

“仅当类型参数是引用类型时才支持变体。”

值类型不支持方差

以下也不编译:

// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.
于 2010-05-06T18:20:26.887 回答