List .AddRange(List )不起作用
我发现无法将具体对象列表添加到接口对象列表中。
public static void AddJob(List masterJobs, List jobs) { masterJobs.AddRange(jobs); //fail to compile }
相反,需要使用以下代码:
public static void AddJob(List masterJobs, List jobs) { masterJobs.AddRange(jobs.Cast()); }
这背后的理性是什么?
Lasse对于为什么这在C#3中不起作用是正确的 – 没有从List
到List
。
在C#4中,它可以工作,不是因为列表是协变的,而是因为IEnumerable
是协变的。 换句话说,代码实际上是:
public static void AddJob(List masterJobs, List jobs) { IEnumerable jobsTmp = jobs; // This is the covariance working masterJobs.AddRange(jobs); // This is now fine }
jobs
实现了IEnumerable
,因此通过协方差有一个IEnumerable
的引用转换,所以一切正常。 对Cast
的调用实际上是在C#3解决方法中执行类似的工作 – 您正在使用它来转换为IEnumerable
。
如果你想了解更多关于generics差异的信息,可以看一下我的NDC 2010演讲video ,或者阅读Eric Lippert的一系列博客文章 。
原因是List
不是List
,尽管Job
实现了IJob
。
这是共同或反对的变化(我永远不会记得哪个是哪个。)
思路是这样的:
编译器不能保证AddRange
只从给定的参数中读取内容,因此无法保证这是安全的,因此无法编译。
例如,对于所有编译器都知道,AddRange可以将另一个对象添加到jobs
参数中,该参数实现IJob
(因为AddRange需要IJob
集合),但不是Job
,这是jobs
期望的,因此这是不安全的。
在C#4.0中,有一些支持处理这个,但我不确定它是否会处理你的特定情况,因为必须在接口级指定支持,而不是在方法级指定。
换句话说,您必须在接口类型上指定与T相关的所有内容仅进入集合,永远不会出现,然后编译器将允许您执行此操作。 但是,您无法阅读的集合将毫无意义。