Collection<T>类及其用法

28

我遇到了以下代码:

var collection = new Collection<string>();

我很少看到使用Collection类,并且找不到太多关于它用途的信息。通过查看.NET Framework源代码,可以发现它实际上只是一个List的包装器,因为它存储了一个List成员字段。它的构造函数如下:

public Collection()
{
  this.items = (IList<T>) new List<T>();
}

它还实现了IList接口。因此,您可以将集合声明为:

IList<string> collection = new Collection<string>();

对我来说,这在功能上与创建一个列表是等效的:

IList<string> collection = new List<string>();

那么在您自己的代码中,何时才需要使用它而不是List?我看到它是其他.NET集合的基类,但为什么会将其包含为公共具体类(相对于内部和/或抽象类)?


关于可能重复的评论 -- 相关问题的答案似乎说Collection类应该用作基类。我真正不同的问题是:

  1. 如果在自己的代码中使用,为什么不使用List作为基类?
  2. 在您自己的代码中,实例化新的Collection代替List真的有意义吗?
  3. 如果它真的只是作为基类提供,为什么它不是抽象的

你正在寻找这个吗?http://msdn.microsoft.com/zh-cn/library/ms132397.aspx - user827992
5个回答

20

我认为MSDN文档本身已经指出了最重要的方面(由我强调):

Collection类提供了受保护的方法,可用于在添加和删除项、清除集合或设置现有项的值时自定义其行为

以及

实现说明:

此基类提供了使实现者更轻松创建自定义集合的功能。鼓励实现者扩展此基类,而不是创建自己的基类

[针对您更新的问题进行编辑]

1. 如果在您自己的代码中使用,为什么不使用List作为基类呢?

Collection<T>提供了一些受保护的方法,因此您可以轻松地覆盖行为并完全注册自己的业务逻辑,例如:

  • ClearItems()
  • InsertItem()
  • RemoveItem()
  • SetItem()

List<T>没有提供这些方法,几乎没有受保护的方法可以覆盖行为,这使得自定义更加困难。

2. 在您自己的代码中初始化新Collection而不是List,真的有意义吗?

不仅仅是为了初始化一些集合而使用它。如果你需要它作为实现你自己逻辑的基类,那就用它。

这将取决于您自己的业务需求。在日常开发工作中,几乎所有情况下,现有 - 并且数量众多 - 的集合应该已经提供了您所需的功能。有类型安全和无类型的集合、线程安全的集合等等,任何您能想到的东西都有。

然而,有一天可能会需要实现一个集合类型,在允许添加/更新/删除任何项之前执行某些有效性检查,或者以特殊方式处理只有稀疏填充列表中的下一项。您永远不知道客户可能会有什么想法。

在这种情况下,创建自己的集合类型可能会有所帮助。


1
谢谢您的回答...我想知道为什么Collection类不是抽象类? - Zaid Masud
1
@ZaidMasud:这只是一个猜测,但我认为这是为了让你不必实现核心功能,并允许你只关注于在基本功能有所不同的地方实现自己的逻辑。 - Jens H
3
一个抽象类可以有非抽象的虚方法,所以你所说的可以通过抽象类实现。 - Zaid Masud

9
Collection(T)类是许多其他集合类的基类。 List类是一个速度优化的类,而Collection类则设计用于可扩展性。
当您查看Collection(T)的成员时,会发现它包含一些额外的受保护虚拟方法(如InsertItem),而List(T)类没有这些方法。
实际上,我创建了一个Collection类的实例。如果可能的话,我只会将此类型用作方法签名中的参数,以避免紧密耦合方法与集合实现。
回答您的问题:
1. 我会使用Collection(T)作为基类,因为该类旨在用作其他集合类型的基类。 它提供更多的灵活性(请参见受保护的虚拟方法InsertItemRemoveItemSetItem)。 2. 当需要集合时,我会使用List(T)类的实例,因为它经过了速度优化。 3. 实际上,我想知道为什么他们没有将Collection(T)类设为抽象类。

7

正如Krzysztof Cwalina 所说

•List没有被设计成可扩展的。例如,您不能覆盖任何成员。这意味着从属性返回List的对象将无法在集合修改时得到通知。Collection允许您重写SetItem受保护成员以在添加新项或更改现有项时“收到通知”。

•List有很多成员在许多场景下都不相关。我们说List对于公共对象模型来说过于“繁忙”。想象一下ListView.Items属性返回具有其所有丰富性的List。现在,看看实际ListView.Items返回类型;它更简单,类似于Collection或ReadOnlyCollection。

因此,FxCop的规则CA1002也告诉您不要公开通用列表,而应使用Collection。还可以参考Code Analysis Team Blog post关于FxCop规则以及为什么返回Collection而不是List的文章。

对于问题1和2,请参见上文。至于问题三,它不仅仅是作为基类存在的。你应该能够实例化它并将其用作返回类型,因此它不是抽象的。

1
你的回答对于问题1和2是有道理的。对于问题3,我不明白你的推理。你不需要实例化某个东西来使用它作为返回类型,抽象类可以用作返回类型。 - Zaid Masud
在阅读了你们的代码分析团队博客文章链接后,我发现这篇博客实际上回答了我的第三个问题(尽管你没有在回答中明确说明原因)。 - Zaid Masud
就像我说的,你应该能够实例化它并将其用作返回类型。我不确定这与您自己接受的答案“同时,您可以将其用作返回类型并直接实例化”有何不同,但是没错。我认为从中派生的能力是从我链接的博客文章中显而易见的。 - Bas

4
我并不喜欢回答自己的问题,但就问题三而言,我相信我理解了其中的推理。在查看Bas Paap的答案中引用的此链接时,我得到了它的答案。
简而言之,Collection类不是抽象的原因是你可能想要保留以后从该类派生的选项。同时,你可以将其用作返回类型并直接实例化。 该链接显示了一个代码示例,在这种情况下使用它是合适的。
我已经为我认为回答了问题提出的问题的所有其他答案投了赞成票。

1
我仍然不相信 Q3。虽然链接的帖子描述了一个可行的情况,但我很难称之为令人满意的答案。我的意思是,问题同样可以通过公开类型为 IList<T> 的成员来解决(据我所知,Collection<T> 不会公开任何额外的成员,除了 IList<T> 所需的成员),而 Collection<T> 是否抽象并没有任何意义。虽然在某些时候能够实例化 Collection<T> 可能很方便,但它有点掩盖了它作为自定义集合的基类的主要目的。 - Grx70

0

尽管枚举接口提供了对集合的前向迭代,但它们没有提供确定集合大小、通过索引访问成员、搜索或修改集合的机制。为此,框架提供了ICollectionIListIDictionary接口来实现这些功能。

ICollection<T>提供中等功能(例如Count属性)。

IList<T>及其非泛型版本提供最大的功能(包括按索引“随机”访问)。

在几乎所有需要编写集合类的情况下,您很少需要实现这些接口。相反,您可以将其子类化为Collection<T>等。

我希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接