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