在下面的一段代码中,我希望能够隐式地从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
是协变的,从Ai
到Bi
- 存在隐式参考或身份转换
Xi
是逆变的,从Bi
到Ai
Xi
是不变的,并且存在从Ai
到Bi
的身份转换
检查我的代码,它似乎与规范一致:
IEnumerable<out T>
是一种接口类型
IEnumerable<out T>
T
是协变
从TBase
到IBase
那么 - 这是C#4编译器中的错误吗?
答案 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.