为什么List <T>在协变接口MyInterface <out T>上无效

Fir*_*oso 9 c# generics inheritance type-inference covariance

跟进前一个问题的问题,这被认为是一个共同变异问题.更进一步,如果我修改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
{
}
Run Code Online (Sandbox Code Playgroud)

我明白了:

方差无效:类型参数T必须在Sandbox.IFactory.MakeStuff()上无效.T是协变的.

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

Eri*_*ert 12

其他答案是正确的,但有理由说明为什么编译器将此标记为不安全.假设我们允许它; 什么可能出错?

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());
Run Code Online (Sandbox Code Playgroud)

嘿,我们刚刚将一个链轮添加到一个只能包含小工具的列表中.

只有一个地方,编译器可以检测到问题,这是在接口的声明中.

对于有点过于乐观的错误消息感到抱歉.我无法想出更好的东西.


Tru*_*ill 7

@Craig的回答是正确的.要解决,请将其更改为:

IEnumerable<T> MakeStuff()
Run Code Online (Sandbox Code Playgroud)

编辑:至于原因,请看IEnumerable <T>接口的定义:

public interface IEnumerable<out T> : IEnumerable
Run Code Online (Sandbox Code Playgroud)

请注意,IList <T>接口没有out关键字.接口和委托中的泛型类型参数支持差异,而不支持类,因此它不适用于List <T>.

  • 请注意,在编写完这个答案之后,发布了一个新版本的.NET Framework,其中`List <T>`实现了一个新接口`IReadOnlyList <out T>`.如图所示,接口是协变的,就像`IEnumerable <out T>`. (2认同)

Cra*_*ntz 6

因为您可以在a中添加和删除对象List<T>,所以T必须始终是不变的,即使列表是函数结果也是如此.执行var l = MakeStuff()完毕后,您可以将内容放入列表中或将其取出,因此T必须保持不变.