无法将源类型“List<Person>”转换为IList<ISomething>

9

这可能是一个相当简单的问题,但有一些东西我没有理解。

假设有以下类:

public class Person : ICloneable {
    public object Clone()
    {
        Console.WriteLine("Hello, world");
        return new Person();
    }
}

为什么这是可以的?
List<Person> people = new List<Person> { new Person() };

IEnumerable<ICloneable> clonables = people;

但这不是吗?
List<Person> people = new List<Person> { new Person() };

IList<ICloneable> clonables = people;

为什么我可以将ICloneable分配给IEnumerable,但不能将ICloneable分配给IList?

7
你收到的错误信息很可能已经告诉了你原因。 - Robert Harvey
IEnumererable<T>是协变的,这就是为什么它能够工作。List<T>不是,也不能成为协变的。 - vcsjones
4
因此,解决方案是:cloneables = people.Cast<ICloneable>().ToList(); - user2160375
不是一个好的解决方案。实际上你正在创建一个新的 List<ICloneable>,而不是重新分配旧的。 - Zev Spitz
3个回答

13

这被称为协变性。Eric Lippert和Jon Skeet等人在回答Difference between Covariance & Contra-variance的问题时,都给出了一些不错的解释,涉及到协变性(以及它的姊妹关系,逆变性)。

简单来说,你可以像枚举一个ICloneable列表一样枚举一个Person列表,不会有任何问题,因为你无法更改枚举。但是你不能将Person列表分配给ICloneable列表,因为这样你以后可能会尝试向其插入其他派生自ICloneable的对象,这将导致强类型安全性严重违反。


7

IList:

public interface IList<T> : ICollection<T>, 
    IEnumerable<T>, IEnumerable

IEnumerable

public interface IEnumerable<out T> : IEnumerable

注意在 IEnumerable 中的 outIEnumerable<T>协变的

2

我有一个不同的答案,但是是错误的。我道歉。感谢马特指出这一点。

错误信息非常误导人,它表明转换将起作用,但实际上并没有。问题在于将Person转换为ICloneable可能需要调整指针,以便通用ICloneable的虚函数表正确。这意味着列表中的每个元素都可能需要进行调整。真正的解决方法是使用ToList:

        IList<ICloneable> clonablesA = people.ToList<ICloneable>();

请忽略以下一些评论,因为我完全删除了我的第一个回答。


2
不要在你的帖子上签名。它们已经有签名了;看到了吗?---------------> - Robert Harvey
1
即使使用显式转换也无效。IList<ICloneable> cloneables = (IList<ICloneable>)people; - Xinbi
错误信息并未指出存在显式转换,也不在此上下文中有效。 - Servy
使用VS 2013,我看到的错误信息是:Error 1 Cannot implicitly convert type 'System.Collections.Generic.List<Cloneable.Person>' to 'System.Collections.Generic.IList<System.ICloneable>'. An explicit conversion exists (are you missing a cast?) C:\Users\Ken\Documents\Visual Studio 2013\Projects\Samples\Cloneable\Cloneable\Program.cs 29 45 Cloneable - KC-NH
1
显式转换不起作用。它会将编译时错误换成运行时异常(InvalidCastException)。 - Matt Burland
@KC-NH:显然是在你尝试进行强制类型转换的地方。 - Matt Burland

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