Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么协方差不适用于泛型方法_C#_C# 4.0_Covariance_Contravariance - Fatal编程技术网

C# 为什么协方差不适用于泛型方法

C# 为什么协方差不适用于泛型方法,c#,c#-4.0,covariance,contravariance,C#,C# 4.0,Covariance,Contravariance,假设我有接口和类: public interface ITree {} public class Tree : ITree {} 由于IEnumerable是协变的,下面的代码行编译成功: IEnumerable<ITree> trees = new List<Tree>(); IEnumerable trees=new List(); 但当我把它放在通用方法中时: public void Do<T>() where T : ITree { I

假设我有接口和类:

public interface ITree {}
public class Tree : ITree {}
由于
IEnumerable
是协变的,下面的代码行编译成功:

IEnumerable<ITree> trees = new List<Tree>();
IEnumerable trees=new List();
但当我把它放在通用方法中时:

public void Do<T>() where T : ITree
{
     IEnumerable<ITree> trees = new List<T>();
}
public void Do(),其中T:ITree
{
IEnumerable trees=新列表();
}
我从编译器获得编译错误:

错误1无法将类型“System.Collections.Generic.List”隐式转换为“System.Collections.Generic.IEnumerable”。存在显式转换(是否缺少转换?)D:\lab\lab.General\lab.General\Program.cs 83 40 lab.General


为什么协方差在这种情况下不起作用?

这是因为协方差只对引用类型(类、接口和委托)起作用。添加一个类约束,它可以正常编译:

public static void Do<T>() where T : class, ITree
publicstaticvoiddo(),其中T:class,ITree

Thank,我错误地认为,在执行约束
T:ITree
时,T是引用类型,但实际上并非如此。如果
ITree
是接口类型,类型为
ITree
的存储位置将始终保持
null
或对实现
ITree
的堆对象的引用,但受限于
ITree
的泛型类型的存储位置可以保存引用,也可以保存实现
ITree
的值类型的实际实例。就个人而言,我不喜欢将实现接口的结构隐式转换为实现这些接口的堆引用,而结构在这方面没有任何发言权。值类型语义在有用的方面不同于重引用语义,但“统一类型系统”模型(错误地)假定它们的行为相同。
List类型的变量。枚举器
实现了
IEnumerable
,但它的行为与
IEnumerable
类型的变量的行为非常不同,后者保存了对
列表的引用。枚举器
@supercat我想你的意思是
IEnumerable
,而不是
IEnumerable
。确实,当您将值类型存储在接口类型的变量中时,它们会被装箱。泛型的好处是,我们不需要拳击。因此,如果您说
Do()
其中
MyStruct
是实现接口的结构,那么您将得到
MyStruct
的方法的特定“版本”。因此,如果在方法内部,它说
T localT=default(T)然后代码生成一个值
MyStruct
,并且不进行装箱,这很酷。但是使用
ITree localT=default(T)当然你会得到一个装箱的值。@JeppeStigNielsen:你是对的。问题是,实现一个变异接口的结构将具有结构语义,但如果转换到该接口,它将成为(并表现为)一个引用类型,具有一个中断的
Equals
方法。虽然有一个可以由值类型实现的接口的概念是有用的,但我不确定说装箱结构隐式实现了原始的接口是否有用;允许结构定义对接口的强制转换(强制转换将产生一个实现接口的新对象)可能更有用