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

这是C#4中的协方差错误吗?,c#,generics,covariance,C#,Generics,Covariance,在下面的代码中,我希望能够从元素隐式转换为基本元素,因为TBase可以隐式转换为IBase public interface IBase { } public interface IDerived : IBase { } public class VarianceBug { public void Foo<TBase>() where TBase : IBase { IEnumerable<TBase> elements = null;

在下面的代码中,我希望能够从
元素
隐式转换为
基本元素
,因为
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;
    }
}
公共接口IBase{}
公共接口IDerived:IBase{}
公共类变量错误
{
public void Foo(),其中TBase:IBase
{
IEnumerable elements=null;
IEnumerable-derivedElements=null;
i可数基元;
//很好
基本元素=衍生元素;
//错误CS0266:无法隐式转换类型
//“System.Collections.Generic.IEnumerable”到
//'System.Collections.Generic.IEnumerable'。
//存在显式转换(是否缺少强制转换?)
基本元素=元素;
}
}
然而,我得到了评论中提到的错误

引用规范:

如果类型
T
是使用变量类型参数
T
声明的接口或委托类型,则类型
T
可以转换为类型
T
,并且对于每个变量类型参数
Xi
,以下情况之一适用:

  • Xi
    是协变的,存在从
    Ai
    Bi

  • Xi
    是逆变的,存在从
    Bi
    Ai

  • Xi
    是不变的,并且存在从
    Ai
    Bi

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

  • IEnumerable
    是一种接口类型

  • IEnumerable
    是用变量类型参数声明的

  • T
    是协变的

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


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

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

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

Marc是对的-我正要粘贴相同的响应

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

从常见问题解答:

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

值类型不支持差异

以下内容也不会编译:

// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.
//int是一种值类型,因此代码不会编译。
IEnumerable objects=新列表();//这里有编译器错误。

当您显式强制转换时会发生什么?编译器说有一个。因为您正在向下转换它,所以它有点意义….?只是为了使它显式化-这是您最后一个不真实的项目符号“存在从TBase到IBase的隐式引用转换”(除非您添加
:class
)。它可能是可分配的,但不一定是引用转换。如果没有
:class
,这是一种“受约束”的转换,这是一种魔术,让相同的IL调用方法(包括属性访问器)以相同的方式对引用类型和值类型进行调用:Charles:你错了-第一个赋值有效(在我的机器(TM)上有效)。Mark:对-我的错。它不是引用类型,因此没有引用转换。然而,我剩下的问题是,为什么第一个赋值有效?好答案——添加类约束有效。然而-这提出了另一个问题:为什么第一个作业有效?@Omer-因为
IBase
IDerived
被视为参考;只有
TBase
尚未确定。@MarcGravel:更准确地说,虽然接口可以由非堆(值)类型实现,但接口类型的存储位置将始终保存堆对象引用。例如,
列表
保存对实现
IEnumerator
的堆对象的引用,而
列表,其中T:IEnumerator
将在
T
List.Enumerator
时保存该结构类型的实例。请注意,如果试图在
列表中存储
列表.枚举器
,系统会将其字段复制到一个新的堆对象并存储对该对象的引用。在其他情况下,泛型的好处就在于:如果类型参数
T
是值类型,那么我们就不会得到装箱。例如,如果
T
恰好是
Int32
,则
列表
不是装箱整数列表。然而,对于接口,我们有装箱,例如
varli=newlist{2,4,6,}
给出了一个框列表。更符合问题的是,以下内容也不起作用:
IEnumerable Compariables=new list()
// int is a value type, so the code doesn't compile.
IEnumerable<Object> objects = new List<int>(); // Compiler error here.