List<T>.AsReadOnly()与IReadOnlyCollection<T>的区别

55
List<T>实现了IReadOnlyCollection<T>接口,并提供了AsReadOnly()方法,该方法返回ReadOnlyCollection<T>(该类也实现了IReadOnlyCollection<T>)。

AsReadyOnly()的使用/原因是什么?它存在的意义在于处理其中一两个极端情况,此时仅将列表作为IReadOnlyCollection<T>返回就不够好了。

起初我认为它可能是为了防止取消const限定,但看起来你可以通过ReadOnlyCollection<T>Items访问器来完成这个操作。

顺便说一下,ReadOnlyCollection<T>类型的文档如下:

提供用于泛型只读集合的基类。

这与描述构造函数的方式相矛盾,该构造函数如下所示:

初始化(...)类的新实例,该实例是指定列表的只读包装。

更新:我没有看到ReadOnlyCollection<T>Items是protected。


我更喜欢使用.ToArray()。在我看来更简单! - Kieren Johnstone
2
@KierenJohnstone 可能也更慢。 - Uwe Keim
2
.ToArray() 创建一个全新的数组,因此速度较慢,并且该数组是可变的,这意味着它的任何元素都可以被更改。 .AsReadOnly() 更快,并且消费者无法通过更改单个元素来改变它。但是当原始列表更改时,它也会发生变化。 - bboyle1234
2个回答

64
如果你只是返回一个实际的 List<T> 作为 IReadOnlyList<T>,那么调用者始终可以将其强制转换回来,然后随心所欲地修改列表。相反,调用AsReadOnly()创建了列表的只读包装器,消费者无法更新。
请注意,只读包装器将反映对基础列表所做的更改,因此具有访问原始列表的代码仍可以使用该知识更新它,使只读版本的任何消费者看到这些更改。

1
但是我可以将 AsReadOnly().Items 强制转换回来,不是吗? - tymtam
7
@Tymek你不能:AsReadOnly()方法会创建一个新的、非List<T>类的对象,该对象没有公共方法允许修改原始列表。由于Items属性是*protected*的,因此普通用户无法访问它。 - dlev
3
@Steven "Items" 是原始列表,但我现在才注意到它是受保护的。哎呀! - tymtam
6
请注意,List<T>.AsReadOnly() 方法自 .NET 2.0 起存在,而 IReadOnlyCollection 接口是在 .NET 4.5 中引入的新功能。因此,在这里 API 可能会有些重叠,但 AsReadOnly 仍然很有用。 - Steven
1
@Steven 是的,自 .NET 2.0 以来,AsReadonly 总是返回 ReadonlyCollection<T>,只是最近实现了 IReadonlyCollection<T> - nawfal
显示剩余2条评论

30
首先,AsReadOnly()并不是因为IReadOnlyList<T>不够好而添加的——IReadOnlyList<T>只能在.NET 4.5及以上版本使用,而AsReadOnly()方法可以在.NET 2中使用。
更重要的是:AsReadOnly()IReadOnlyList<T>有非常不同的用途。 ReadOnlyCollection<T>用于实现对象模型,例如像Dictionary<K,V>.KeysDictionary<K,V>.Values这样的东西。这适用于消费者不应该能够更改内容而生产者可以的情况。它与提供所有者验证更改或执行副作用的钩子的Collection<T>协同工作。
另一方面,IReadOnlyList<T>只是一个接口,提供了集合的只读视图。方法可以使用它来表示“我需要一个随机访问集合,但我不需要能够修改它”。例如,BinarySearch方法可能看起来像这样:
public int BinarySearch<T>(IReadOnlyList<T> list, int start, int length);
为了使这种方法有用,需要能够传入任何List。强制创建包装集合将是极其昂贵的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接