协方差和 IList
我想要一个协变集合,其项目可以通过索引检索。 IEnumerable 是我所知道的唯一一个协变的 .net 集合,但它没有此索引支持。
具体来说,我想这样做:
List<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs;
IList<Animal> animalList = dogs; // This line does not compile
现在,我知道为什么这是一个问题。 List 实现了具有 Add 方法的 ICollection
。通过向上转换为 Animals 的 IList
,它将允许后续代码添加“真实”List
集合中不允许的任何类型的动物。
那么有人知道支持索引查找并且也是协变的集合吗?我不想创建自己的。
I would like a Covariant collection whose items can be retrieved by index. IEnumerable is the only .net collection that I'm aware of that is Covariant, but it does not have this index support.
Specifically, I'd like to do this:
List<Dog> dogs = new List<Dog>();
IEnumerable<Animal> animals = dogs;
IList<Animal> animalList = dogs; // This line does not compile
Now, I'm aware of why this is a problem. List implements ICollection
that has an Add method. By up casting to IList
of Animals, it would allow subsequent code to add any type of animal which is not allowed in the "real" List<Dog>
collection.
So is anyone aware of a collection that supports index lookups that is also covariant? I would like to not create my own.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
更新:从 .NET 4.5 开始,有
IReadOnlyList
和IReadOnlyCollection
两者都是协变的;后者基本上是IEnumerable
加上Count
;前者添加了T this[int index] {get;}
。还应该注意的是,IEnumerable
从 .NET 4.0 开始是协变的。List
和ReadOnlyCollection
(通过List.AsReadOnly()
)实现这两个。它只能是协变的,如果它只有一个
get
索引器,即但所有主集合都有
{get;set;}
,这使得这很尴尬。我不知道有什么足够的,但你可以包装它,即编写一个扩展方法:它是一个
IList
的包装器,仅公开IEnumerable
和get
索引器...?应该只需要几分钟的工作...Update: from .NET 4.5 onwards there is
IReadOnlyList<out T>
andIReadOnlyCollection<out T>
which are both covariant; The latter is basicallyIEnumerable<out T>
plusCount
; the former addsT this[int index] {get;}
. It should also be noted thatIEnumerable<out T>
is covariant from .NET 4.0 onwards.Both
List<T>
andReadOnlyCollection<T>
(viaList<T>.AsReadOnly()
) implement both of these.It can only be covariant if it only has a
get
indexer, i.e.But all main collections have
{get;set;}
, which makes that awkward. I'm not aware of any that would suffice there, but you could wrap it, i.e. write an extension method:which is a wrapper around an
IList<T>
that only exposes theIEnumerable<T>
and theget
indexer...? should be only a few minutes work...下面是我为解决这种情况而编写的一个类:
现在您可以编写如下代码:
更改在两个列表中都可见,因为实际上仍然只有 1 个列表。适配器类只是传递调用,根据需要转换项目以实现所需的
IList
接口。显然,如果你向
animalList
添加除 Dogs 之外的任何内容,它都会抛出异常,但这满足了我的需求。Here's a class I wrote to address this scenario:
Now you can write code like this:
The changes are visible in both lists, because there's really still only 1 list. The adapter class just passes the calls through, casting items as necessary to achieve the desired
IList<TBase>
interface.Obviously, if you add anything but Dogs to
animalList
, it will throw an exception, but this met my needs.从技术上讲,有数组集合。它的差异有点被破坏,但它满足了你的要求。
当然,如果您尝试将
Tiger
放入数组中的任何位置,那么在运行时您会发生相当惊人的爆炸。Technically, there's the array collection. It's sort of broken in its variance, but it does what you ask.
You will, of course, blow up rather spectacularly at runtime if you try to put a
Tiger
in the array anywhere.从 .NET Framework 4.5 开始,存在一个协变接口 IReadOnlyList。它本质上与 Mark Gravell 的答案中的 IIndexedEnumerable 接口相同。
IReadOnlyList 的实现如下:
As of .NET Framework 4.5, there exists an interface IReadOnlyList which is covariant. It is essentially the same as the IIndexedEnumerable interface in Mark Gravell's answer.
IReadOnlyList is implemented like this: