【问题标题】:Why and when to inherit from Collection<T>为什么以及何时从 Collection<T> 继承
【发布时间】:2026-02-13 20:15:02
【问题描述】:

我正在跟踪我的项目中用 C# 编写的遗留代码。

我找到以下代码:

public class FooCollection : Collection<IFoo> {};

我不明白为什么(以及何时)我们需要像这样创建自己的 Collection 类。

为什么不直接使用内置集合(arrayList&lt;T&gt;Dictionary&lt;K,V&gt;、...)

Collection&lt;T&gt; 中使用的数据结构是什么??大批?还是链表?

【问题讨论】:

  • 通过子类化泛型,可以将其功能扩展到 vanilla 集合为您提供的功能之外。您能否更新标题以传达inherit from Collection&lt;T&gt; 的要求?
  • 如果派生类没有添加新方法,那么这确实只是死代码。
  • 也许这会帮助你弄清楚最初的开发者在想什么。 *.com/questions/398903/…

标签: c# data-structures collections


【解决方案1】:

通过声明您继承 Collection&lt;T&gt; 类,您声明您的类 IS-A Collection&lt;T&gt;,这意味着您已经实现了所有it's API(通过派生类或基于Collection&lt;T&gt; 类)。

继承的优点是您可以决定重写某些方法并以您认为更适合您的需要或类型T(在您的情况下为IFoo)的方式处理它们。

以同样的方式,您还可以决定扩展您的 API 以支持您认为适合您的情况的其他一些功能。

例如,如果您的课程 IFoo 看起来像这样:

public interface IFoo
{
   int a;
   int b;
}

在您的派生类中,您可以向Remove 添加一个重载,如下所示:

public bool Remove(int a, int b )...

它将删除所有出现的具有特定值的项目 ab

【讨论】:

    【解决方案2】:

    我不明白为什么(以及何时)我们需要像这样创建自己的 Collection 类。

    你并不真的“需要”;你可以直接使用Collection&lt;IFoo&gt;,但是有一个特定的类可以提高可读性。

    此外,它允许您为此集合类型添加特定行为,因为Collection&lt;T&gt; 类允许通过覆盖虚拟方法来重新定义大多数操作;这允许您自定义标准收集方法的行为。

    因此,例如,虽然您不能直接覆盖 Add 方法,但您可以覆盖 InsertItem,然后由 AddAddRange 等使用。

    为什么不直接使用内置集合(数组、列表、字典……)

    数组的长度是固定的,所以你不能添加或删除项目;所以它不是真的等价的。 Dictionary&lt;K,V&gt; 将值与键相关联,因此它具有明显不同的用途。 List&lt;T&gt; 可以改为使用,只要您不需要自定义行为即可;这个类不是为继承而设计的,因为它的方法是非虚拟的。

    Collection 中使用的数据结构是什么?大批?还是链表?

    Collection&lt;T&gt; 充当另一个 IList&lt;T&gt; 的包装器。默认情况下,它使用List&lt;T&gt;(基于数组),但您可以将任何其他IList&lt;T&gt; 实现传递给构造函数,它会使用它。

    【讨论】:

    • "另外,Collection 类的大多数方法都是虚拟的" 真的。你应该再读一遍。它的方法几乎都不是虚拟的。
    • @Servy 他们是。例如,Add 在内部调用受保护的 InsertItem 虚拟方法。同样的清除,删除...等。
    • @ken2k 正如我在回答中所描述的那样,恰好有四种虚拟方法(object 之外)。那肯定不是“大多数”方法。 绝大多数不是虚拟的,根据我的经验,虚拟的极少数方法不足以进行任何有意义的扩展。
    • @Servy Add 在调用InsertItem 时所做的事情。对于其他虚拟方法,ClearInsertRemoveRemoveAt 相同。通过覆盖InsertItem,您也可以完全更改Add 的逻辑。
    • @Servy,你说得对,我的话具有误导性。我的意思是大多数操作(不是方法)都可以被覆盖(添加/设置/删除/清除)。我会更新我的答案。
    【解决方案3】:

    我不明白为什么(以及何时)我们需要像这样创建自己的 Collection 类。

    我永远不会。我个人认为Collection 类根本没有用。我还没有找到它的用途。

    类型的主要问题之一是大多数方法不是虚拟的。唯一的虚拟方法是 ClearItemsInsertItemRemoveItemSetItem(以及来自 ObjectEqualsGetHashCodeToString)。其余的方法/属性不是虚拟的,因此不能被覆盖。这意味着您可以稍微更改添加/插入/删除项目的语义,但仅此而已。

    Collection&lt;T&gt;使用的数据结构是什么?

    在内部,它由 List&lt;T&gt; 支持。

    为什么不直接使用内置集合(arrayList&lt;T&gt;Dictionary&lt;K,V&gt;、...)

    几乎在所有情况下你都会这样做。虽然在某些情况下您可能实际上想要创建一个全新的集合类型,但我从未发现Collection&lt;T&gt; 有助于创建这样的自定义集合。通常,最好根据您的集合可以实现的内容,在 Collections 命名空间中实现一些接口,并且如果它只是现有集合的扩展,则可以选择使用其他集合作为实例字段来组合类型。

    【讨论】:

    • “这意味着您可以稍微更改添加/插入/删除项目的语义,但仅此而已”:您通常还对集合做什么?
    • 此外,默认情况下它由 List 支持,但您实际上可以将任何其他 IList 实现传递给构造函数。
    • @ThomasLevesque 你改变了底层数据的访问方式;例如,影响该数据的视图,而不是更改基础数据本身。或者您对基础数据的表示方式进行了一些更重大的更改,这对您没有帮助。
    • 是的,这是你可以做的。您不能使用 Collection 做所有事情,但它仍然涵盖了许多场景。如果您想要比 Collection 允许的更多的自定义,您仍然可以直接实现 IList,但是在很多情况下继承 Collection 就足够了。事实上,.NET 框架中的许多类都继承自它。
    • @ThomasLevesque 程序员应该首先定义自己的集合的情况并不多。如果这是您经常使用的东西,您可能应该使用更多组合而不是继承。创建一个新的collection应该是非常少见的。当然,对于基础语言库来说,这将不那么罕见。如果对集合所做的更改很小且很微妙,这通常是一个非常强烈的信号,表明您不应该首先使用继承。
    【解决方案4】:

    我不明白为什么(以及何时)我们需要像这样创建自己的 Collection 类。

    实现自定义Collection&lt;T&gt; 的主要用例之一是序列化。

    如果要自定义集合的序列化方式(for example to XML, using XmlSerializable),可以创建自定义集合类并在其上实现IXmlSerializable。然后,您可以通过操作该接口的ReadWrite 方法来更改读取或写入集合的方式。

    class MyCollection : Collection<string>, IXmlSerializable
    {
        public XmlSchema GetSchema()
        {
            return(null);
        }
    
        public void WriteXml(XmlWriter writer)
        {
            //// foreach (var item in Items)
                //// writer.Write...
        }
    
        public void ReadXml(XmlReader reader)
        {
            //// Items.Add(reader.Read...
        }
    }
    

    在大多数情况下,与在父类上实现 IXmlSerializable 相比,这更可取,因为它可以减少整体代码并促进重用(您可以在多个地方重用同一个集合,并且它将以与只要你使用XmlSerializer 来序列化它)。

    我最近使用了这个,因为外部系统需要集合的每个元素来生成一个 XML 节点,并附加元素的索引。

    最终的结构有点像这样:

    <MyCollection>
      <Item1>first item</Item1>
      <Item2>second item</Item2>
      ...
    </MyCollection>
    

    我不喜欢像这样将集合与序列化耦合,但有时这是一种有效的方法。

    【讨论】: