【问题标题】:Casting generic container of type to container of inherited type?将类型的通用容器转换为继承类型的容器?
【发布时间】:2011-11-13 23:32:12
【问题描述】:

如果我有两个班级:

public class A { }
public class B : A { }

我创建了一个通用容器和一个接受它的函数:

public void Foo(List<A> lst) { ... }

如果我尝试将 List&lt;B&gt; 转换为 List&lt;A&gt;,则会得到无效转换,而必须像这样传递它:

var derivedList = new List<B>();
Foo(new List<A>(derivedList));

是否有某种方法可以将List&lt;B&gt; 传递给此函数,而无需分配全新列表的开销,或者 C# 是否不支持从派生类型的通用容器转换为其基类型?

【问题讨论】:

标签: c# .net list generics inheritance


【解决方案1】:

List&lt;B&gt; 只是不是List&lt;A&gt; - 毕竟,您可以将普通的A 添加到List&lt;A&gt;,但不能添加到List&lt;B&gt;

如果您使用 C# 4 和 .NET 4 并且您的 Foo 方法只需要遍历列表,然后将方法更改为:

public void Foo(IEnumerable<A> lst) { ... }

在 .NET 4 中,IEnumerable&lt;T&gt;T 中的协变,它允许从IEnumerable&lt;B&gt;(包括List&lt;B&gt;)到IEnumerable&lt;A&gt; 的转换。这是安全的,因为值只会“流出”IEnumerable&lt;A&gt;

要更详细地了解这一点,您可以观看我在 NDC 2010 上作为torrent of NDC 2010 videos 的一部分提供的会议视频。

【讨论】:

    【解决方案2】:

    这是不可能的。 C# 不支持具体类型(例如 List&lt;T&gt;)的 co / contra 变化。它确实在接口上支持它,所以如果你将Foo 切换到以下签名,你可以避免分配

    public void Foo(IEnumerable<A> enumerable) { ...
    

    【讨论】:

    • 啊,这就是为什么代码 var newList = new List&lt;A&gt;(derivedList) 因为它需要一个 IEnumerable&lt;A&gt;... 我应该能够将它从 List 更改为 IList 并且它应该可以正常工作, 然后。谢谢!
    【解决方案3】:

    如果您希望将类似列表的内容传递给将读取它们但不写入它们的例程,则可以定义一个通用协变 IReadableList 接口,以便 IReadableList 可以是传递给期望 IReadableList 的例程。不幸的是,现有的常见 IList 实现没有实现任何这样的东西,因此实现一个的唯一方法是实现一个包装类(它可以接受一个 IList 作为参数),但它可能不会太难。这样的类还应该实现非泛型 IList,也是只读的,以允许代码评估 Count 而不必知道列表中项目的类型。

    请注意,对象对 IReadableList 的实现不应被视为任何不变性的承诺。让读写列表或包装类实现 IReadableList 是完全合理的,因为读写列表是可读。不可能使用 IReadableList 修改列表而不将其转换为其他内容,但不能保证作为 IReadableList 传递的列表不能以其他方式修改,例如将其转换为其他内容,或使用存储在其他地方的引用。

    【讨论】:

      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 2011-06-04
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多