【问题标题】:Why can't you cast from IList<IParent> to IList<IChild> where IChild implements IParent [duplicate]为什么不能从 IList<IParent> 转换为 IList<IChild> IChild 实现 IParent [重复]
【发布时间】:2009-11-10 20:23:25
【问题描述】:

可能重复:
IList<Type> to IList<BaseType>

我正在使用 .NET 2.0 在 C# 中编程,但我不明白为什么下面的强制转换会导致空引用。

如果你有一个 IList,为什么不能将它转换为 IChild 实现 IParent 的 IList

using System.Collections.Generic;

namespace InterfaceTest
{
    public interface IParent
    {
    }

    public interface IChild : IParent
    {
    }

    public abstract class Parent : IParent
    {
    }

    public sealed class Child : Parent, IChild
    {
    }

    public sealed class Container
    {
        public IList<IChild> ChildInterfaceList
        {
            get;
            set;
        }

        public Container()
        {
            ChildInterfaceList = new List<IChild>();
        }
    }

    class Program
    {
    static void Main(string[] args)
    {
            Container container = new Container();

            var childInterfaceList = container.ChildInterfaceList;

            System.Diagnostics.Debug.Assert(childInterfaceList != null);

            var parentInterfaceList = container.ChildInterfaceList as IList<IParent>;

            //I don't expect parentInterfaceList to be null, but it is
            System.Diagnostics.Debug.Assert(parentInterfaceList != null);
        }
    }
}

【问题讨论】:

标签: c# generics inheritance list interface


【解决方案1】:

C# 可变集合不支持集合元素类型的变化。想想如果你这样做会发生什么:

IList<IChild> kids = new List<IChild> {
    new Egg("Shelly"), new Egg("Egbert"), new Egg("Yoko")
};

var parents = kids as IList<IParent>;

parents.Add(new Sloth("Sid")); // what would happen here?

如果转换成功,parents 的运行时类型仍将是 List&lt;IChild&gt;,它不会接受未实现 IChild 的内容,并且必须抛出异常。

可接受的转换是:

using System.Linq;
var parents = kids.Cast<IParent>().ToList();

这将创建原始列表的副本,但使用List&lt;IParent&gt; 作为其运行时类型。

C# 4.0 支持泛型变体,但不能安全地将可变集合制成变体。只有像IEnumerable 这样的纯只读接口可以安全地进行协变,而纯只写接口(有吗?)可以安全地进行逆变。

【讨论】:

  • 是的,有。例如, IComparer 实际上是一个用于方差目的的只写接口。 T进去了,结果出来了,但T从来没有出来。可以比较任意两只动物的比较器也可以比较任意两只长颈鹿,所以 IComparer 可以逆变换为 IComparer
  • 不保证 IEnumerable 类型的对象是只读的。 IEnumerable someList = new List(); //someList 非常可写 :)
  • 正确,但无关紧要。无法通过 IEnumerable 获取变异方法,这是相关的。这一事实使我们能够安全地使 IE 在 T 中协变。
  • @Eric Lippert:我想我混淆了我的方差方向——逆变反转了类型之间的窄比关系,使 IComparer 到 IComparer 的转换扩大了。我认为使用强制转换试图扩大问题中的 type parameter 让我感到困惑。适合我在生病时发帖。 :)
【解决方案2】:

这是一个常见的陷阱。

考虑这个简单的例子解释:

.NET 中的所有内容都继承自 Object,对吗?所以,让我们假设你想要的都是可能的......

List<int> ints = new List<int>();
List<object> objects = ints as List<object>;
objects.Add("Hello there!");

您刚刚尝试将string 添加到实际的整数列表中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    相关资源
    最近更新 更多