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

时间:2010-05-06 17:49:06

标签: 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;
    }
}

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

引自规范:

  

如果T<A1, …, An>是使用变体类型参数T<B1, …, Bn>声明的接口或委托类型,则T类型可以变换为类型T<X1, …, Xn>,并且每个变体类型参数Xi包含以下其中一项:

     
      
  • Xi是协变的,从AiBi

  • 存在隐式参考或身份转换   
  • Xi是逆变的,从BiAi

  • 存在隐式参考或身份转换   
  • Xi是不变的,并且存在从AiBi的身份转换

  •   

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

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

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

  • T是协变

  • TBaseIBase

  • 存在隐式参考转化

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

2 个答案:

答案 0 :(得分:50)

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

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

因为我可以写一个:

public struct Evil : IBase {}

答案 1 :(得分:14)

Marc是对的 - 我正准备粘贴相同的回复。

参见Covariance&amp; Contravariance FAQ:

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.