如果我有两个班级:
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#不支持从派生类型的通用容器转换为基类型?
答案 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;不能以其他方式修改,例如通过将其转换为其他方式,或使用存储在别处的引用。