171

代码如下所示:

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

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

4

8 回答 8

249

要回答关于为什么不的问题的“为什么”部分List<T>,原因是面向未来和 API 的简单性。

面向未来

List<T>没有被设计成通过子类化来轻松扩展;它旨在快速用于内部实现。你会注意到它上面的方法不是虚拟的,因此不能被覆盖,并且它的 // 操作中Add没有钩子InsertRemove

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

因此,通过返回可以轻松子类化的类Collection<T>或接口IList<T>ICollection<T>或者IEnumerable<T>您可以将内部实现更改为不同的集合类型以满足您的需求,而不会破坏消费者的代码,因为它仍然可以返回为他们期望的类型。

API 简单性

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

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

于 2008-11-07T11:45:24.417 回答
51

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

于 2008-11-07T10:29:33.023 回答
6

我认为还没有人回答“为什么”部分……所以就这样吧。“为什么”您“应该”使用 aCollection<T>而不是 aList<T>的原因是因为如果您公开 a List<T>,那么任何可以访问您的对象的人都可以修改列表中的项目。而Collection<T>应该表明您正在制作自己的“添加”、“删除”等方法。

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

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

public int[] MyIntegers { get; }

你会认为因为只有一个“get”访问器没有人可以弄乱这些值,但事实并非如此。任何人都可以像这样更改里面的值:

someObject.MyIngegers[3] = 12345;

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

于 2008-11-07T10:41:45.137 回答
2

它主要是关于抽象您自己的实现,而不是公开 List 对象以直接操作。

让其他对象(或人)直接修改对象的状态并不是一个好习惯。想想属性获取器/设置器。

集合 -> 对于普通集合
ReadOnlyCollection -> 对于不应修改的集合
KeyedCollection -> 当你想要字典时。

如何修复它取决于你希望你的类做什么以及 GetList() 方法的目的。你能详细说明吗?

于 2008-11-07T11:09:56.813 回答
1

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

于 2008-11-07T11:11:25.343 回答
1

有一些东西要补充,尽管自从被问到这个问题已经很长时间了。

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

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

于 2015-11-11T16:08:39.157 回答
0

我认为返回类似的东西没有任何问题

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

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

但这取决于我期望的消费者类型——如果这是一个类似于数据网格的东西,我更喜欢返回它,无论如何在大多数情况下IEnumerable<TItem> 它都是复制的项目列表:)

于 2012-01-14T03:14:01.460 回答
-1

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

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

//using System.Collections.ObjectModel;

Collection<MyClass> myCollection = new Collection<MyClass>(myList);
于 2008-11-07T10:35:16.773 回答