什么是集合语义(在.NET中)?

我需要在自己的类中维护集合语义。 难道你不解释,什么是集合语义? 据我所知,它是一组必须在类中实现的一些接口。 这是真的吗? 如果是 – 必须在课堂上实现什么为什么 ? 这两个接口–ICollection和IEnumerable – 是否足够,或者这些只是最必要的接口?

我正在编写一个循环链表,使用这篇文章作为帮助。

.NET中有许多集合类型,它们都有一些共同的行为,例如:

  • 您可以使用foreach枚举它们
  • 他们有一个Count属性
  • 您可以使用Add方法添加项目
  • 等等…

这种行为是从集合类型中得到的,你猜对了:它都在ICollection接口中。 我们来看看接口层次结构:

  • IEnumerable允许使用foreach枚举您的类
  • ICollection是表示集合的IEnumerable
    • 它允许检索项目Count
    • 您可以Add / Remove / Clear集合中的项目
    • 集合只能是只读的,在这种情况下, IsReadOnly应该返回true
    • 还有其他一些辅助方法: Contains / CopyTo
  • IListICollection ,允许通过索引访问项目。
    • 它添加了一个索引器
    • 一些与索引相关的函数: Insert / RemoveAt
    • IndexOf

您应该实现哪个接口是语义问题

IEnumerable只是一个可枚举的序列 。 它只应通过使用代码进行一次枚举,因为您永远不知道它在多个枚举中的行为方式。 像ReSharper这样的工具甚至会在多次枚举IEnumerable发出警告。
当然,大多数时候你可以安全地多次枚举它,但有时你不应该。 例如,枚举可以执行SQL查询(例如,想想Linq-to-SQL)。

您可以通过定义一个函数来实现IEnumerableGetEnumerator ,它返回en IEnumerator 。 枚举器是一个对象,它是一种指向序列中当前元素的指针。 它可以返回此Current值,并可以使用MoveNext移动到下一个元素。 它也是一次性的(它由foreach在枚举结束时处理)。

让我们分解一个foreach循环:

 IEnumerable sequence = ... // Whatever foreach (T item in sequence) DoSomething(item); 

这相当于以下内容:

 IEnumerator enumerator = null; try { enumerator = sequence.GetEnumerator(); while (enumerator.MoveNext()) { T item = enumerator.Current; DoSomething(item); } } finally { if (enumerator != null) enumerator.Dispose(); } 

对于记录,实现IEnumerable并不是严格要求使类可用于foreach 。 鸭子打字就在这里已经足够了,但我太过分了。

当然,您可以使用yield关键字轻松实现模式:

 public static IEnumerable GetAnswer() { yield return 42; } 

这将创建一个私有类,它将为您实现IEnumerable ,因此您不必这样做。

ICollection表示一个集合,可以安全地多次枚举。 但你真的不知道它是什么样的集合。 它可以是一个集合,一个列表,一个字典,等等。

这是集合语义。

一些例子:

  • T[] – 即使你不能Add / Remove它也会实现ICollection
  • List
  • HashSet – 集合的一个很好的例子,但不是列表
  • Dictionary – 是的,那是ICollection>
  • LinkedList
  • ObservableCollection

IList让您知道集合是允许您通过索引轻松访问元素的类型(即在O(1)时间内)。

这不是你的循环链表的情况,因为它不仅需要O(n)时间,而且首先没有有意义的索引。

一些例子:

  • T[]
  • List
  • ObservableCollection

请注意,例如, HashSetDictionary不再位于列表中。 这些不是列表。 LinkedList在语义上是一个列表,但它在O(1)时间内不提供索引访问(它需要O(n) )。

我应该提一下,在.NET 4.5中有只读的等价物: IReadOnlyCollectionIReadOnlyList 。 这些对他们提供的协方差很有帮助。