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

bov*_*ium 152 .net c# collections code-analysis

代码如下所示:

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

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

警告3 CA1002:Microsoft.Design:更改'IMyClass.GetList()'中的'List'以使用Collection,ReadOnlyCollection或KeyedCollection

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

Gre*_*ech 219

要回答问题的"原因"部分,为什么不这样做List<T>,原因是面向未来和API简单化.

面向未来

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

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

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

API简单

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

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

  • 我看到你的第一点,但我不知道我是否同意你的API简单部分. (7认同)
  • http://blogs.msdn.com/fxcop/archive/2006/04/27/faq-why-does-donotexposegenericlists-recommend-that-i-expose-collection-lt-t-gt-instead-of-list- lt-t-gt-david-kean.aspx-&gt;已死。我们是否有更新的信息? (2认同)

Jon*_*eet 49

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

  • @Jon:我知道这已经过时了,但你可以在http://blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx上评论Krzysztof所说的内容吗?特别是他的评论,"我们建议使用Collection <T>,ReadOnlyCollection <T>或KeyedCollection <TKey,TItem>作为输出和属性,接口IEnumerable <T>,ICollection <T>,IList <T>用于输入.CA1002似乎与Krzysztof的评论一致.我无法想象为什么会推荐具体的集合而不是接口,以及为什么输入/输出之间的区别. (8认同)
  • 我认为这与歧义有关.`Collection <T>`和`ReadOnlyCollection <T>`都来自`ICollection <T>`(即没有`IReadOnlyCollection <T>`).如果你返回界面,它不明显是哪一个,是否可以修改.无论如何,感谢您的意见.这对我来说是一个很好的理智检查. (8认同)
  • @Nelson:很少你想*要求*调用者传入一个不可变列表,但是*return*one是合理的,因此他们知道它肯定是不可变的.虽然不确定其他收藏品.有更多细节会很高兴. (3认同)
  • 这不是针对特定情况的.显然,通常`ReadOnlyCollection <T>`对输入没有意义.类似地,`IList <T>`作为输入说,"我需要Sort()或IList所具有的其他成员"对输出没有意义.但我的意思是,为什么`ICollection <T>`被推荐作为输入而`Collection <T>`作为输出.为什么*不*使用`ICollection <T>`作为输出也如你所建议的那样? (2认同)

Tim*_*uri 5

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

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

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

public int[] MyIntegers { get; }
Run Code Online (Sandbox Code Playgroud)

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

someObject.MyIngegers[3] = 12345;
Run Code Online (Sandbox Code Playgroud)

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