Collection<T> 与 List<T> 您应该在界面上使用啥?
Posted
技术标签:
【中文标题】Collection<T> 与 List<T> 您应该在界面上使用啥?【英文标题】:Collection<T> versus List<T> what should you use on your interfaces?Collection<T> 与 List<T> 您应该在界面上使用什么? 【发布时间】:2010-09-21 06:22:09 【问题描述】:代码如下:
namespace Test
public interface IMyClass
List<IMyClass> GetList();
public class MyClass : IMyClass
public List<IMyClass> GetList()
return new List<IMyClass>();
当我运行代码分析时,我得到以下建议。
警告 3 CA1002:Microsoft.Design:将“IMyClass.GetList()”中的“List”更改为使用 Collection、ReadOnlyCollection 或 KeyedCollection
我应该如何解决这个问题,这里有什么好的做法?
【问题讨论】:
【参考方案1】:要回答问题的“为什么”部分,为什么不 List<T>
,原因是面向未来和 API 的简单性。
面向未来
List<T>
并非旨在通过继承它来轻松扩展;它旨在快速用于内部实现。你会注意到它上面的方法不是虚拟的,因此不能被覆盖,并且它的Add
/Insert
/Remove
操作中没有钩子。
这意味着如果您将来需要更改集合的行为(例如拒绝人们尝试添加的空对象,或者在发生这种情况时执行额外的工作,例如更新您的类状态),那么您需要将返回的集合类型更改为可以子类化的集合类型,这将是一个破坏性的接口更改(当然,更改诸如不允许 null 之类的语义也可能是一个接口更改,但是诸如更新内部类状态之类的事情不会)。
因此,通过返回可以轻松子类化的类(例如 Collection<T>
)或接口(例如 IList<T>
、ICollection<T>
或 IEnumerable<T>
),您可以将内部实现更改为不同的集合类型以满足您的需求需要,而不会破坏消费者的代码,因为它仍然可以作为他们期望的类型返回。
API 简单性
List<T>
包含很多有用的操作,如BinarySearch
、Sort
等。但是,如果这是您要公开的集合,那么您很可能控制列表的语义,而不是消费者。因此,虽然您的类在内部可能需要这些操作,但您的类的消费者不太可能想要(甚至应该)调用它们。
因此,通过提供更简单的集合类或接口,您可以减少 API 用户看到的成员数量,并使他们更易于使用。
【讨论】:
此响应已结束。可以在此处找到有关该主题的另一本好书:web.archive.org/web/20090608080454/http://blogs.msdn.com/fxcop/… 我看到了你的第一点,但我不知道我是否同意你的 API 简单性部分。 ***.com/a/398988/2632991 这也是一篇非常好的帖子,关于集合和列表之间的区别。 工作链接:blogs.msdn.microsoft.com/kcwalina/2005/09/26/… 我在下面看到 Skeet 本人提倡接口(至少在撰写本文时)。但是我看到很多铁杆支持者认为返回类型应该尽可能具体和具体。您是否也认为接口仅适用于当您真正看到可能返回不同列表类型的具体情况时。还是您觉得有理由默认使用接口?【参考方案2】:有些东西要补充,虽然很久没有人问了。
当您的列表类型派生自 List<T>
而不是 Collection<T>
时,您无法实现 Collection<T>
实现的受保护虚拟方法。
这意味着如果对列表进行任何修改,您的派生类型将无法响应。这是因为List<T>
假定您在添加或删除项目时知道。能够响应通知是一种开销,因此 List<T>
不提供它。
如果外部代码可以访问您的集合,您可能无法控制何时添加或删除项目。因此Collection<T>
提供了一种了解您的列表何时被修改的方法。
【讨论】:
【参考方案3】:我认为返回类似
的内容没有任何问题this.InternalData.Filter(crteria).ToList();
如果我返回了内部数据的断开副本,或者数据查询的分离结果 - 我可以安全地返回 List<TItem>
而不会暴露任何实现细节,并允许在方便的方式。
但这取决于我期望的消费者类型 - 如果这是一个类似于数据网格的东西,我更愿意返回 IEnumerable<TItem>
这将是在大多数情况下复制的项目列表 :)
【讨论】:
【参考方案4】:我认为还没有人回答“为什么”部分......所以就这样吧。 “为什么”您“应该”使用Collection<T>
而不是List<T>
的原因是因为如果您公开List<T>
,那么任何可以访问您的对象的人都可以修改列表中的项目。而Collection<T>
应该表明您正在制作自己的“添加”、“删除”等方法。
您可能不需要担心,因为您可能只是为自己(或者可能是几个同事)编写界面。这是另一个可能有意义的例子。
如果你有一个公共数组,例如:
public int[] MyIntegers get;
您可能会认为,因为只有一个“get”访问器,没有人可以弄乱这些值,但事实并非如此。任何人都可以像这样更改里面的值:
someObject.MyIngegers[3] = 12345;
就个人而言,在大多数情况下,我只会使用List<T>
。但是,如果您正在设计一个要分发给随机开发人员的类库,并且您需要依赖对象的状态......那么您将想要制作自己的 Collection 并从那里锁定它: )
【讨论】:
"如果您将 ListCollection 类实际上只是其他集合的包装类,用于隐藏它们的实现细节和其他特性。我认为这与面向对象语言中的属性隐藏编码模式有关。
我认为你不必担心,但如果你真的想取悦代码分析工具,只需执行以下操作:
//using System.Collections.ObjectModel;
Collection<MyClass> myCollection = new Collection<MyClass>(myList);
【讨论】:
抱歉,打错了。我表示 CollectionCollection<T>
中既不保护自身也不保护底层集合。
那段代码是为了“取悦代码分析工具”。我不认为@TamasCzinege 在任何地方说过使用Collection<T>
会立即保护您的基础集合。【参考方案6】:
在这种情况下,我通常会尝试公开所需的最少实现量。如果消费者不需要知道您实际上正在使用列表,那么您不需要返回列表。通过按照 Microsoft 的建议返回 Collection,您可以隐藏您正在使用您的类的消费者的列表这一事实,并将它们与内部更改隔离开来。
【讨论】:
【参考方案7】:这主要是关于将您自己的实现抽象出来,而不是公开 List 对象以供直接操作。
让其他对象(或人)直接修改对象的状态不是一个好习惯。想想属性 getter/setter。
收藏 -> 普通收藏 ReadOnlyCollection -> 对于不应修改的集合 KeyedCollection -> 当你想要字典时。
如何修复它取决于你希望你的类做什么以及 GetList() 方法的目的。能详细点吗?
【讨论】:
但是 CollectionReadOnlyCollection
其他两个不服从。
指的是“良好实践”。请适当的上下文。下面的列表只是说明了此类类型的基本要求,因为 OP 想要了解警告。然后我继续问他GetList()
的目的,以便能够更正确地帮助他。
好的,我理解那部分。但在我看来,推理部分仍然不合理。您说Collection<T>
有助于抽象内部实现并防止直接操作内部列表。如何? Collection<T>
只是一个包装器,对传递的同一个实例进行操作。这是一个用于继承的动态集合,仅此而已(格雷格的回答在这里更相关)。【参考方案8】:
我个人会声明它返回一个接口而不是一个具体的集合。如果你真的想要列表访问,请使用IList<T>
。否则,请考虑ICollection<T>
和IEnumerable<T>
。
【讨论】:
IList 是否会扩展 ICollection 接口? @Jon:我知道这已经过时了,但你能评论一下 Krzysztof 在blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx 上所说的话吗?特别是他的评论,We recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs.
CA1002 似乎与 Krzysztof 的 cmets 一致。我无法想象为什么会推荐一个具体的集合而不是一个接口,以及为什么输入/输出之间的区别。
@Nelson:您很少要求调用者传入一个不可变的列表,但是返回一个让他们知道是合理的它绝对是不可变的。虽然不确定其他系列。如果有更多细节会很高兴。
不是针对特定情况的。显然,一般ReadOnlyCollection<T>
对输入没有意义。同样,IList<T>
作为输入说,“我需要 Sort() 或 IList 具有的其他成员”,这对输出没有意义。但我的意思是,为什么推荐ICollection<T>
作为输入而Collection<T>
作为输出。为什么不也按照您的建议使用ICollection<T>
作为输出?
我认为这与明确性有关。 Collection<T>
和 ReadOnlyCollection<T>
都派生自 ICollection<T>
(即没有 IReadOnlyCollection<T>
)。如果返回接口,看不出来是哪一个,能不能修改。无论如何,感谢您的意见。这对我来说是一个很好的健全性检查。以上是关于Collection<T> 与 List<T> 您应该在界面上使用啥?的主要内容,如果未能解决你的问题,请参考以下文章
remove-duplicates-from-sorted-list (删除)
使用 List<T> 和公开 Collection<T> 的最佳方式