关于泛型:这是C#4中的协方差错误吗?

Is this a covariance bug in C# 4?

在下面的代码中,我希望能够隐式地从elements转换为baseElements,因为TBase隐式地转换为IBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
    }
}

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

从规范中引用:

A type T is variance-convertible to a type T if T is either an interface or a delegate type declared with the variant type parameters T, and for each variant type parameter Xi one of the following holds:

  • Xi is covariant and an implicit reference or identity conversion exists from Ai to Bi

  • Xi is contravariant and an implicit reference or identity conversion exists from Bi to Ai

  • Xi is invariant and an identity conversion exists from Ai to Bi

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

  • IEnumerable是接口类型

  • IEnumerable用变量类型参数声明

  • T是协变的

  • 存在从TBaseIBase的隐式引用转换。

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


差异仅适用于引用类型(或存在标识转换)。不知道TBase是引用类型,除非添加: class

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

因为我可以写:

1
public struct Evil : IBase {}


马克是对的-我正要贴同样的回答。

请参见协方差和反方差常见问题解答:

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

从常见问题:

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

值类型不支持差异

以下内容也无法编译:

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