集合<t>与List <t>您应该在接口上使用什么?</t> </t>

时间:2008-11-07 10:28:08

标签: c# .net collections code-analysis

代码如下所示:

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

我该如何解决这个问题以及这里有什么好习惯?

8 个答案:

答案 0 :(得分:212)

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

<强>面向未来

List<T>的目的不是通过子类化来轻松扩展;它旨在快速进行内部实施。您会注意到它上面的方法不是虚拟的,因此无法覆盖,并且Add / Insert / Remove操作没有挂钩。

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

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

API简洁性

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

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

答案 1 :(得分:47)

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

答案 2 :(得分:1)

我认为没有人回答“为什么”的部分......所以这里有。 “为什么”您“应该”使用Collection<T>而不是List<T>的原因是因为如果您公开List<T>,那么任何有权访问您对象的人都可以修改名单。而Collection<T>应该表示您正在制作自己的“添加”,“删除”等方法。

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

如果你有一个公共阵列,例如:

public int[] MyIntegers { get; }

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

someObject.MyIngegers[3] = 12345;

就个人而言,我会在大多数情况下使用List<T>。但是如果你正在设计一个你要向随机开发者提供的类库,并且你需要依赖于对象的状态......那么你将需要创建自己的Collection并从那里锁定它: )

答案 3 :(得分:1)

主要是关于抽象你自己的实现,而不是直接暴露List对象。

让其他对象(或人)直接修改对象的状态并不是一种好习惯。想想物业的吸气者/制定者。

收藏 - &gt;正常收集
ReadOnlyCollection - &gt;对于不应修改的集合
KeyedCollection - &gt;当你想要字典时。

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

答案 4 :(得分:1)

在这种情况下,我通常会尝试暴露所需的最少量的实施。如果消费者不需要知道您实际使用的是列表,则无需返回列表。通过返回,因为Microsoft建议使用Collection,您可以隐藏这样一个事实,即您正在使用类中消费者的列表,并将其与内部更改隔离开来。

答案 5 :(得分:1)

有些东西需要补充,虽然这已经有很长一段时间了。

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

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

答案 6 :(得分:0)

我没有看到返回类似

之类的任何问题
this.InternalData.Filter(crteria).ToList();

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

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

答案 7 :(得分:-1)

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

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

//using System.Collections.ObjectModel;

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