List<IJob>.AddRange(List<Job>)无法工作。

5
我发现具体对象列表无法添加到接口对象列表中。
public static void AddJob(List<IJob> masterJobs, List<Job> jobs)
{
    masterJobs.AddRange(jobs);  //fail to compile
}

相反,需要使用以下代码:

public static void AddJob(List<IJob> masterJobs, List<Job> jobs)
{
    masterJobs.AddRange(jobs.Cast<IJob>());  
}

这背后的理由是什么?

6
Skeet 、协变/逆变在三...两...一... - user1228
如果您能够使用C#4.0,本程序将可用。 - Martin Ingvar Kofoed Jensen
1
我假设上面第二个例子中的 //fail to compile 注释是从第一个例子中遗留下来的?还是那个也编译失败了? - Lasse V. Karlsen
@Lasse,是的,这是一个遗留问题,已经被修复了。 - Graviton
2个回答

10

Lasse关于为什么这在C# 3中不起作用是正确的 - 没有从List<IJob>List<Job>的转换。

在C# 4中,它将起作用,不是因为list是协变的,而是因为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
}

jobs 实现了 IEnumerable<Job>,因此通过协变性有一个到 IEnumerable<IJob> 的引用转换,所以一切正常。对 Cast<T> 的调用在你的 C# 3 解决方法中实际上正在执行类似的工作 - 你正在使用它将其转换为 IEnumerable<IJob>

如果您想要更多了解泛型的差异性,可以观看我在 NDC 2010 上的演讲的视频或阅读 Eric Lippert 的 博客系列


所以,让我来澄清一下...他们使用inout来表示逆变/协变,因为它本质上描述了该类仅“接收”泛型类型的实例(即方法参数),或者如果该类仅“传递”类的实例(即方法的返回值)?IEnumerable<T>不“接收”任何T,因此将该类标记为<out T>是安全的,因此泛型类型是协变的?IList<T>既接收T也传递T,这使得它是不变的? - user1228
@Will:是的,完全正确。请参阅Eric Lippert关于变异性的博客文章系列以获取更多信息。我将编辑我的帖子并链接到该文章。 - Jon Skeet
喔,我想我搞定了!我想我搞定了!(接音乐) - user1228

8
原因在于 List<IJob> 不是 List<Job>,尽管 Job 实现了 IJob 接口。这是协变或逆变(我总是记不清哪个是哪个)的原因。
思路大致如下:编译器无法保证 AddRange 仅从给定的参数中读取内容,因此它无法保证安全性,因此会导致编译失败。
例如,对于编译器而言,AddRange 可能会添加另一个实现了 IJob 接口但不是 Job 的对象到 jobs 参数中,因为 AddRange 需要 IJob 集合,但这并不安全,因为 jobs 期望的是 Job。在 C# 4.0 中,有一些处理方法,但我不确定它是否适用于您的特定情况,因为支持必须在接口级别上指定,而不是在方法级别上指定。
换句话说,您必须在接口类型上指定所有与 T 相关的内容只进入集合而不离开集合,然后编译器才允许您这样做。然而,无法从中读取的集合将毫无意义。

1
“out” 是协变,而 “in” 是逆变;现在是 IList<out T>,因此是协变的。(但我错了,请见下文) - user1228
哦,不错,我之前没有看到过如此简短的这两个的总结,谢谢! - Lasse V. Karlsen
这位作者详细介绍了4.0版本中关于协变性和逆变性泛型支持的内容:http://www.buunguyen.net/blog/new-features-of-csharp-4.html - user1228
1
@Will:不,IList<T>仍然是不变的,因为它既有进又有出的值。但是,IEnumerable<T>是协变的。 - Jon Skeet
现在我知道为什么了。哇,当所有事情都恰到好处时,那种感觉真是太棒了。 - user1228
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接