这是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.