简体   繁体   English

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

[英]Casting generic container of type to container of inherited type?

If I have two classes: 如果我有两节课:

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

and I create a generic container and a function that takes it: 然后创建一个通用容器和一个使用它的函数:

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

I get an invalid conversion if I attempt casting a List<B> to a List<A> , and instead have to pass it like so: 如果我尝试将List<B>强制转换为List<A> ,则会得到无效的转换,而必须像这样传递它:

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

Is there some way to pass a List<B> to this function without the overhead of allocating a brand new list, or does C# not support converting from a generic container of a derived type to its base type? 是否有某种方法可以将List<B>传递给此函数而无需分配全新的列表,或者C#不支持从派生类型的泛型容器转换为其基本类型?

A List<B> simply isn't a List<A> - after all, you can add a plain A to a List<A> , but not to a List<B> . List<B>根本不是 List<A> -毕竟,您可以将普通A添加到List<A> ,但不能添加到List<B>

If you're using C# 4 and .NET 4 and your Foo method only really needs to iterate over the list, then change the method to: 如果您使用的是C#4和.NET 4, 并且 Foo方法只真正需要遍历列表,则将方法更改为:

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

In .NET 4, IEnumerable<T> is covariant in T , which allows a conversion from IEnumerable<B> (including a List<B> ) to IEnumerable<A> . 在.NET 4中, IEnumerable<T>T协变的,它允许从IEnumerable<B> (包括List<B> )转换为IEnumerable<A> This is safe because values only ever flow "out" of IEnumerable<A> . 这是安全的,因为值仅从IEnumerable<A>流出。

For a much more detailed look at this, you can watch the video of the session I gave at NDC 2010 as part of the torrent of NDC 2010 videos . 要对此进行更详细的了解,您可以观看我在NDC 2010上的会议视频,作为NDC 2010视频洪流的一部分。

This is not possible. 这是不可能的。 C# doesn't support co / contra variance on concrete types such as List<T> . C#不支持诸如List<T>类的具体类型的co / contra差异。 It does support it on interfaces though so if you switch Foo to the following signature you can avoid an allocation 它确实在接口上支持它,因此,如果将Foo切换到以下签名,则可以避免分配

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

If you wish to pass list-like things to routines which are going to read them but not write them, it would be possible to define a generic covariant IReadableList<out T> interface, so that an IReadableList<Cat> could be passed to a routine expecting an IReadableList<Animal>. 如果您希望将类似列表的内容传递给将要读取但不编写它们的例程,则可以定义通用协变IReadableList <out T>接口,以便将IReadableList <Cat>传递给例程需要IReadableList <Animal>。 Unfortunately, common existing IList<T> implementations don't implement any such thing, and so the only way to implement one would be to implement a wrapper class (which could accept an IList as a parameter), but it probably wouldn't be too hard. 不幸的是,现有的常见IList <T>实现并没有实现任何此类事情,因此,实现该问题的唯一方法是实现一个包装类(可以接受IList作为参数),但可能不会太难。 Such a class should also implement non-generic IList, also as read-only, to allow code to evaluate Count without having to know the type of the items in the list. 这样的类还应该实现非通用IList(也为只读),以使代码无需知道列表中项目的类型即可对Count进行评估。

Note that an object's implementation of IReadableList<T> should not be regarded as any promise of immutability. 注意,对象的IReadableList <T>的实现不应视为任何不变性的保证。 It would be perfectly reasonable to have a read-write list or wrapper class implement IReadableList<T>, since a read-write list is readable . 具有读写列表或包装器类实现IReadableList <T>是完全合理的,因为读写列表是可读的 It's not possible to use an IReadableList<T> to modify a list without casting it to something else, but there's no guarantee a list passed as IReadableList<T> can't be modified some other way, such as by casting it to something else, or by using a reference stored elsewhere. 无法使用IReadableList <T>修改列表而不将其强制转换为其他内容,但不能保证通过的列表无法通过其他方式修改IReadableList <T>,例如将其强制转换为其他方式,或使用其他位置存储的引用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM