关于c#:Collection< T>

Collection<T> versus List<T> what should you use on your interfaces?

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

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

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

Warning 3 CA1002 : Microsoft.Design : Change 'List' in 'IMyClass.GetList()' to use Collection, ReadOnlyCollection or KeyedCollection

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


为了回答问题的"为什么"部分,即为什么不使用List,原因是未来验证和API的简单性。

未来打样

List的设计不是为了通过对其子类化而易于扩展;它的设计是为了快速地进行内部实现。您会注意到它上的方法不是虚拟的,因此不能被重写,并且它的Add/Insert/Remove操作没有钩子。

这意味着,如果将来需要更改集合的行为(例如,拒绝人们试图添加的空对象,或者在发生这种情况时执行其他工作,例如更新类状态),则需要更改返回到可以子类的集合的类型,这将是一个中断的接口更改(C我们更改诸如不允许空值之类的东西的语义也可能是一个接口更改,但是像更新内部类状态这样的事情是不允许的)。

因此,通过返回一个很容易被子类化的类(如Collection)或一个接口(如IListICollectionIEnumerable,您可以将内部实现更改为不同的集合类型以满足您的需要,而不必破坏消费者的代码,因为它仍然可以作为他们所期望的类型返回。叮叮声。

简单API

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

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


我将亲自声明它以返回接口而不是具体的集合。如果您真的想要访问列表,请使用IList。否则,考虑ICollectionIEnumerable


还有一些需要补充的东西,尽管这已经很久没有被问到了。

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

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


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


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

让其他对象(或人)直接修改对象的状态是不好的做法。想想财产获得者/设定者。

采集->正常采集readOnlyCollection->用于不应修改的集合keyedcollection->当您需要字典时。

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


我认为还没有人回答"为什么"这一部分…所以这里是。为什么"你"应该使用Collection而不是List,是因为如果你公开List,那么任何能够访问你的对象的人都可以修改列表中的项目。而Collection应该表示您正在创建自己的"添加"、"删除"等方法。

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

如果有公共数组,例如:

1
public int[] MyIntegers { get; }

您可能会认为,因为只有一个"get"访问器,没有人可以处理这些值,但这不是真的。任何人都可以这样改变里面的值:

1
someObject.MyIngegers[3] = 12345;

就我个人而言,大多数情况下我只使用List。但是如果你正在设计一个类库,你要把它交给随机的开发人员,你需要依赖于对象的状态…那么你就要自己制作收藏,并从那里锁定它:)


我看不出退货有什么问题

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

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

但这取决于我所期望的消费者类型——如果这是类似于数据网格的东西,我宁愿返回IEnumerable,在大多数情况下,这将是项目的复制列表:)


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

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

1
2
3
//using System.Collections.ObjectModel;

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