将类型的通用容器转换为继承类型的容器?

时间:2011-09-09 17:26:00

标签: c# .net list generics inheritance

如果我有两个班级:

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

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

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

如果我尝试将List<B>转换为List<A>,我会收到无效的转化,而是必须按原样传递:

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

有没有办法将List<B>传递给这个函数,而没有分配全新列表的开销,或者C#不支持从派生类型的通用容器转换为基类型?

3 个答案:

答案 0 :(得分:5)

List<B>只是不是 List<A> - 毕竟,您可以向A添加普通List<A>,但不能到List<B>

如果你正在使用C#4和.NET 4 你的Foo方法只需要迭代列表,那么将方法更改为:

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

在.NET 4中,T中的IEnumerable<T>协变,允许从IEnumerable<B>(包括List<B>)转换为{ {1}}。这是安全的,因为值只会“流出”IEnumerable<A>

有关详细信息,您可以观看我在NDC 2010上作为torrent of NDC 2010 videos一部分的会话视频。

答案 1 :(得分:1)

这是不可能的。 C#不支持List<T>等具体类型的co / contra方差。它确实在接口上支持它,因此如果您将Foo切换到以下签名,则可以避免分配

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

答案 2 :(得分:0)

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

注意对象的IReadableList&lt; T&gt;的实现。不应被视为任何不可变性的承诺。由于读写列表是可读,因此具有读写列表或包装类实现IReadableList&lt; T&gt;是完全合理的。不可能使用IReadableList&lt; T&gt;修改列表而不将其转换为其他内容,但不保证列表作为IReadableList传递&lt; T&gt;不能以其他方式修改,例如通过将其转换为其他方式,或使用存储在别处的引用。