将问题跟进a previous question,这已被确定为协方差问题。更进一步,如果我修改IFactory
如下:
class Program
{
static void Main(string[] args)
{
IFactory<IProduct> factory = new Factory();
}
}
class Factory : IFactory<Product>
{
}
class Product : IProduct
{
}
interface IFactory<out T> where T : IProduct
{
List<T> MakeStuff();
}
interface IProduct
{
}
我明白了:
无效方差:类型参数T必须在Sandbox.IFactory.MakeStuff()上不变地有效。 T是协变的。
为什么这不是无效的?如何/应该如何解决?
答案 0 :(得分:11)
其他答案是正确的,但推断为什么编译器将此标记为不安全是有益的。假设我们允许它;什么可能出错?
class Sprocket: Product {}
class Gadget : Product {}
class GadgetFactory : IFactory<Gadget>
{
public List<Gadget> MakeStuff()
{
return new List<Gadget>() { new Gadget(); }
}
}
... later ...
IFactory<Gadget> gf = new GadgetFactory();
IFactory<Product> pf = gf; // Covariant!
List<Product> pl = pf.MakeStuff(); // Actually a list of gadgets
pl.Add(new Sprocket());
嘿,我们刚刚将一个链轮添加到一个只能包含小工具的列表中。
只有一个地方,编译器可以检测到问题,这是在接口的声明中。
对于有点过于乐观的错误消息感到抱歉。我无法想出更好的东西。
答案 1 :(得分:7)
@Craig's answer是正确的。要解决,请将其更改为:
IEnumerable<T> MakeStuff()
编辑:至于原因,请看IEnumerable<T> Interface的定义:
public interface IEnumerable<out T> : IEnumerable
请注意,IList<T> Interface没有out关键字。接口和委托中的泛型类型参数支持差异,而不支持类,因此它不适用于List&lt; T&gt;。
答案 2 :(得分:6)
因为您可以在List<T>
中添加和删除对象,所以T
必须始终是不变的,即使列表是函数结果也是如此。执行var l = MakeStuff()
后,您可以将内容放入列表中或将其删除,因此T
必须是不变的。