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());