Collection < T > 与 List < T > 在接口上应该使用什么?

代码如下:

namespace Test
{
public interface IMyClass
{
List<IMyClass> GetList();
}


public class MyClass : IMyClass
{
public List<IMyClass> GetList()
{
return new List<IMyClass>();
}
}
}

当我运行代码分析时,我得到以下建议。

警告3 CA1002: Microsoft。设计: 更改“ IMyClass”中的“ List”。使用 Collection、 ReadOnlyCollection 或 KeyedCollection

我应该如何解决这个问题,什么是良好的做法?

104161 次浏览

我个人会声明它返回一个接口,而不是一个具体的集合。如果您真的想要访问列表,请使用 IList<T>。否则,考虑 ICollection<T>IEnumerable<T>

Collection 类实际上只是一个包装其他集合的包装器类,用于隐藏它们的实现细节和其他特性。我认为这与面向对象语言中的属性隐藏编码模式有关。

我认为您不必担心,但是如果您真的想要取悦代码分析工具,只需要执行以下操作:

//using System.Collections.ObjectModel;


Collection<MyClass> myCollection = new Collection<MyClass>(myList);

我觉得还没有人回答“为什么”这部分... 所以我们开始吧。“为什么”您“应该”使用 Collection<T>而不是 List<T>的原因是,如果您公开了 List<T>,那么任何访问您的对象的人都可以修改列表中的项。而 Collection<T>应该表明您正在创建自己的“添加”、“删除”等方法。

您可能不需要担心这个问题,因为您可能只为自己(或者可能为少数同事)编写接口代码。这是另一个可能有意义的例子。

如果您有一个公共数组,例如:

public int[] MyIntegers { get; }

您可能会认为,因为只有一个“ get”访问器,所以没有人能够扰乱这些值,但这是不正确的。任何人都可以改变里面的值,就像这样:

someObject.MyIngegers[3] = 12345;

就个人而言,在大多数情况下,我只会使用 List<T>。但是如果你正在设计一个类库,你要给随机的开发者,你需要依赖于对象的状态... 然后你会想要制作你自己的集合,并从那里锁定它:)

它主要是将您自己的实现抽象出来,而不是将 List 对象暴露给直接操作。

让其他对象(或人员)直接修改对象的状态是不好的做法。

收集-> 一般收集
ReadOnlyCollection-> 用于不应修改的集合
KeyedCollection-> 当你需要字典的时候。

如何修复它取决于您希望您的类做什么以及 GetList ()方法的用途。你能解释一下吗?

在这种情况下,我通常试图公开所需的最少量的实现。如果消费者不需要知道您实际上正在使用一个列表,那么您就不需要返回一个列表。通过按 Microsoft 建议的 Collection 返回,您可以隐藏您正在使用类的使用者的列表的事实,并针对内部更改将它们隔离开来。

为了回答为什么不是 List<T>的问题中的“为什么”部分,原因是防止将来出现问题和 API 的简单性。

防止未来发生

List<T> 的设计不是为了通过子类化它来方便地进行扩展,而是为了内部实现的快速性而设计的。您将注意到它上面的方法不是虚方法,因此不能被重写,并且没有连接到它的 Add/Insert/Remove操作的钩子。

这意味着,如果你需要在将来改变集合的行为(例如拒绝人们试图添加的 null 对象,或者在发生这种情况时执行额外的工作,比如更新类状态) ,那么你需要改变返回的集合类型为子类,这将是一个破坏性的接口改变(当然,改变诸如不允许 null 这样的语义也可能是一个接口改变,但是更新内部类状态这样的事情不会)。

因此,通过返回一个可以很容易被子类化的类,比如 Collection<T>,或者一个接口,比如 IList<T>ICollection<T>或者 IEnumerable<T>,你可以改变你的内部实现为一个不同的集合类型来满足你的需求,而不用破坏消费者的代码,因为它仍然可以被返回为他们期望的类型。

简单

List<T> 包含许多有用的操作,如 BinarySearchSort等。但是,如果这是您正在公开的集合,那么很可能您控制的是列表的语义,而不是使用者。因此,虽然您的类内部可能需要这些操作,但是您的类的消费者不太可能希望(甚至应该)调用它们。

因此,通过提供一个更简单的集合类或接口,可以减少 API 用户看到的成员数量,并使他们更容易使用。

我不觉得返回类似这样的东西有什么问题

this.InternalData.Filter(crteria).ToList();

如果我返回一个内部数据的断开的 收到,或者一个数据查询的分离结果-我可以安全地返回 List<TItem>而不暴露任何实现细节,并允许以方便的方式使用返回的数据。

但这取决于我所期望的消费者类型——如果这是一个类似于数据网格的东西,那么在大多数情况下我更愿意返回 IEnumerable<TItem> 这将是项目的复制清单:)

有些东西需要补充,虽然已经很久没有人问过这个问题了。

当列表类型派生自 List<T>而不是 Collection<T>时,无法实现 Collection<T>实现的受保护的虚方法。 这意味着如果对列表进行任何修改,则派生类型无法响应。这是因为 List<T>假定您在添加或删除项目时已经知道。能够响应通知是一个开销,因此 List<T>没有提供它。

在外部代码可以访问集合的情况下,您可能无法控制何时添加或删除项。因此,Collection<T>提供了一种知道列表何时被修改的方法。