C#: List<T>和Collection<T>之间的区别(CA1002,不要暴露泛型列表)

102
尝试在这个项目上运行代码分析时,出现了一些警告,类似于以下内容:
CA1002:Microsoft.Design:将“List<SomeType>”更改为使用Collection、ReadOnlyCollection或KeyedCollection中的“SomeClass.SomeProtectedOrPublicProperty”
我为什么要使用 Collection 而不是 List?当我查看 MSDN 文档时,它们似乎几乎相同。在阅读警告的错误帮助后,我发现:
System.Collections.Generic.List(T) 是一个专为性能而非继承设计的通用集合,因此不包含任何虚成员。
但这真正意味着什么?我应该做些什么呢?
我应该在内部继续使用 List,然后在属性中返回 new Collection(someList) 吗?还是应该直接开始使用 Collection 而不是 List?

2
请参阅此博客文章以获取详细说明。 - Thomas Levesque
1
顺便提一下,如果你想知道为什么Collection在System.Collections.ObjectModel中,请阅读Krzysztof Cwalina的文章。 - Dan Diplo
1
@ThomasLevesque的链接已失效,但博客文章现在可在此处获得:https://blogs.msdn.microsoft.com/codeanalysis/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collectiont-instead-of-listt-david-kean/ - Ben Randall
1
丹的链接也已经失效。ObjectModel的历史现在在这里:https://learn.microsoft.com/en-us/archive/blogs/kcwalina/the-reason-why-collection-readonlycollection-and-keyedcollection-were-moved-to-system-collections-objectmodel-namespace - Arthur Ward
2个回答

146
简而言之,泛型列表没有Add、Remove等虚拟方法,因为它的设计目的是快速而不是可扩展。这意味着您不能将此具体实现替换为有用的子类(尽管您可以将其子类化,因为它未被封闭)。
因此,通过公开列表本身,您永远无法扩展集合以跟踪添加或删除操作(例如),而不违反该类的公共契约。
通过将你的集合公开为IList或某个类似的接口,你仍然可以使用List作为实际的后备存储,但你保留了未来的可扩展性,因为你可以在不改变类的公共契约的情况下随时更换具体的实现。

14
FYI,Collection<T> 内部使用了一个 List<T> 实例。 - Zoltán Tamási

26

Collection 暴露了一些虚成员(insert,remove,set,clear),您可以重写它们并提供附加功能(例如通知事件),以便在更改集合时使用。

您现在可能不需要这个功能,但是对于包含集合的类来说,这是一个常见的要求,因此最好事先计划。由于 Collection 是为可扩展性而设计的,因此非常灵活。如果以后您决定在集合中需要一些额外的功能,则可以直接扩展它,而不需要更改类的公共接口。如果您使用了列表,则必须将其更改为集合,这意味着会破坏调用您的类的所有调用方,因为他们必须更改为使用列表才能正常工作。

另一方面,List 是为性能而设计的,因此只应在对性能非常重要的特定情况下使用。因为它不可扩展,所以对使用列表的任何内容进行未来更改都会破坏依赖它的所有其他内容。通常应仅在非常低级别的类内部使用 List,并且不要向外部公开,以减少未来破坏更改的机会。


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