这是C#4中的协方差错误吗?

Ome*_*Mor 37 c# generics covariance

在下面的代码我希望能够隐式地从投elements至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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

引用规范:

A型T<A1, …, An>是方差-转换为一个类型T<B1, …, Bn>是否T是一个接口或与变体类型参数声明的委托类型T<X1, …, Xn>,并且对于每个变体类型参数Xi的下面保持一个:

  • Xi是协变和隐式引用或标识转换从存在Ai于Bi

  • Xi是逆变和隐式引用或标识转换从存在Bi于Ai

  • Xi是不变的,并且一个标识转换从存在Ai于Bi

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

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

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

  • T 是协变的

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

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

Mar*_*ell 51

方差仅适用于引用类型(或存在标识转换).TBase除非您添加: class以下内容,否则不知道它是引用类型:

 public void Foo<TBase>() where TBase : class, IBase
Run Code Online (Sandbox Code Playgroud)

因为我可以写一个:

public struct Evil : IBase {}
Run Code Online (Sandbox Code Playgroud)

  • @Omer - 因为`IBase`和`IDerived`*被视为参考; 只有`TBase`尚未决定. (3认同)

Bit*_*der 14

马克是对的 - 我正准备粘贴相同的回复.

请参阅Covariance&Contravariance常见问题解答:

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

来自FAQ:

"仅当类型参数是引用类型时才支持差异."

值类型不支持差异

以下内容无法编译:

// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.
Run Code Online (Sandbox Code Playgroud)