关于C#:什么是集合语义(在.NET中)?

What is collection semantics (in .NET)?

我需要在我自己的类中维护集合语义。你不能解释一下,集合的语义是什么?据我所知,它是一组必须在类中实现的接口。是真的吗?如果是,我必须在课堂上具体实现什么?为什么?这两个接口——ICollection和IEnumerable——足够了吗?还是这只是最需要的接口?

我正在使用本文作为帮助编写循环链接列表。


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

  • 你可以用foreach来列举它们。
  • 他们拥有Count财产。
  • 您可以使用Add方法添加项目
  • 等。。。

这种行为应该来自集合类型,您猜对了:它都在ICollection接口中。让我们看看接口层次结构:

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

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

IEnumerable只是一个可枚举序列。它只能通过使用代码枚举一次,因为您永远不知道它在多个枚举中的行为。如果多次枚举IEnumerable,resharper等工具甚至会发出警告。当然,大多数时候您可以安全地多次枚举它,但有时您不应该这样做。例如,枚举可以执行SQL查询(例如,请考虑linq to sql)。

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

让我们分解一个foreach循环:

1
2
3
IEnumerable<T> sequence = ... // Whatever
foreach (T item in sequence)
    DoSomething(item);

这相当于以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IEnumerator<T> enumerator = null;
try
{
    enumerator = sequence.GetEnumerator();
    while (enumerator.MoveNext())
    {
        T item = enumerator.Current;
        DoSomething(item);
    }
}
finally
{
    if (enumerator != null)
        enumerator.Dispose();
}

就记录而言,实现IEnumerable并不严格要求使类与foreach一起使用。鸭子打字在这里已经足够了,但我太离题了。

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

1
2
3
4
public static IEnumerable<int> GetAnswer()
{
    yield return 42;
}

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

ICollection表示一个集合,可以安全地多次枚举。但你不知道它是什么样的收藏。它可以是一套,一张清单,一本字典,随便什么。

这是集合语义。

一些例子:

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

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

循环链接列表并非如此,因为它不仅需要O(N)时间,而且一开始就没有任何有意义的索引。

一些例子:

  • T[]
  • List
  • ObservableCollection

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

我应该提到在.NET 4.5中有只读的等价物:IReadOnlyCollectionIReadOnlyList。这些对于它们提供的协方差来说是很好的。