这是C#4中的协变错误吗?
在下面baseElements
代码中,我希望能够隐式地从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
是用variablestypes参数T<X1, …, Xn>
声明的接口或委托types,则typesT<A1, …, An>
可以方差转换为T<B1, …, Bn>
并且对于每个变体types参数Xi
,下列之一成立:
Xi
是协变的,并且存在从Ai
到Bi
的隐式参考或身份转换
Xi
是逆变的,并存在着从Bi
到Ai
的隐含的参照或身份转换
Xi
是不变的,从Ai
到Bi
存在一个身份转换
检查我的代码,看起来与规范一致:
-
IEnumerable<out T>
是一个接口types -
IEnumerable<out T>
用variablestypes参数声明 -
T
是协变的 -
存在从
TBase
到IBase
的隐式参考转换
那么 – 这是C#4编译器中的一个错误吗?
差异仅适用于参考types(或者存在标识转换)。 不知道TBase
是引用types,除非你添加: class
:
public void Foo<TBase>() where TBase : class, IBase
因为我可以写一个:
public struct Evil : IBase {}
马克是正确的 – 我正要贴上相同的回应。
请参阅协变和逆变常见问题解答:
http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
从常见问题解答:
“仅当types参数是引用types时才支持差异”。
值types不支持差异
以下不编译:
// int is a value type, so the code doesn't compile. IEnumerable<Object> objects = new List<int>(); // Compiler error here.