C# 为什么列表<;T>;在协变接口MyInterface上无效<;out T>;

C# 为什么列表<;T>;在协变接口MyInterface上无效<;out T>;,c#,generics,inheritance,covariance,type-inference,C#,Generics,Inheritance,Covariance,Type Inference,跟进问题,这已被确定为一个协方差问题。更进一步,如果我修改IFactory,如下所示: class Program { static void Main(string[] args) { IFactory<IProduct> factory = new Factory(); } } class Factory : IFactory<Product> { } class Product : IProduct { } interf

跟进问题,这已被确定为一个协方差问题。更进一步,如果我修改
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
{
}
类程序
{
静态void Main(字符串[]参数)
{
i工厂=新工厂();
}
}
类别工厂:IFactory
{
}
类别产品:IPProduct
{
}
接口IFactory,其中T:IPProduct
{
列出MakeStuff();
}
接口IPProduct
{
}
我得到:

无效差异:类型参数T必须在Sandbox.IFactory.MakeStuff()上始终有效。协变就是协变


为什么这不是一成不变的?如何解决这个问题?

因为可以从
列表中添加和删除对象,
T
必须始终保持不变,即使列表是函数结果。执行
var l=MakeStuff()
之后,您可以将内容放入列表或取出,因此
T
必须是不变的。

是正确的。要解决此问题,请将其更改为:

IEnumerable<T> MakeStuff()
IEnumerable MakeStuff()
编辑:关于原因,请查看以下定义:

公共接口IEnumerable:IEnumerable

请注意,没有out关键字。接口和委托中的泛型类型参数支持差异,而不是类,因此它不适用于列表。

其他答案是正确的,但解释编译器将其标记为不安全的原因很有帮助。假设我们允许它;会出什么问题

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());
类链轮:产品{}
类小工具:产品{}
工厂类别:IFactory
{ 
公共列表MakeStuff()
{ 
返回新列表(){new Gadget();}
}
}
... 后来。。。
IFactory gf=新小工具工厂();
IFactory pf=gf;//协变的!
List pl=pf.MakeStuff();//实际上是一个小工具列表
pl.Add(新链轮());
嘿,我们刚刚在一个只能包含小工具的列表中添加了一个链轮

编译器只能在一个地方检测问题,那就是接口声明


对于有点过于行话化的错误消息,很抱歉。我想不出更好的了。

MakeStuff
使用不同的返回类型。感谢您的反馈,我感谢您提供更多的背景信息,似乎我没有像我所想的那样理解co和对冲。总是很高兴有更多的背景知识,现在发布您的不变性系列的第4部分:-请注意,在编写了这个答案之后,发布了一个新版本的.NET Framework,其中
List
实现了一个新的接口
IReadOnlyList
。如前所述,接口是协变的,就像
IEnumerable
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());