为什么List <t>在协变接口上无效MyInterface <out t =“”> </out> </t>

时间:2011-06-17 19:19:21

标签: c# generics inheritance covariance type-inference

将问题跟进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是协变的。

为什么这不是无效的?如何/应该如何解决?

3 个答案:

答案 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必须是不变的。

相关问题