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

4
我需要在自己的类中保持集合语义。你能解释一下什么是集合语义吗?据我理解,它是一组必须在类中实现的接口。这是真的吗?如果是,那么我需要在类中实现什么以及为什么?这两个接口——ICollection和IEnumerable——足够了吗,还是只有它们是最必要的?
我正在使用这篇文章来帮助编写循环链表。

该链接文章已经实现了ICollection。 - H H
是的,我知道。这让我有点困惑。@HenkHolterman - Mstislav Toivonen
1个回答

12
.NET中有许多集合类型,它们都共享一些常见的行为,例如:
- 您可以使用foreach枚举它们 - 它们具有Count属性 - 您可以使用Add方法添加项目 - 等等...
这些行为是从一个集合类型中预期的,你猜对了:这全部都在ICollection<T>接口中。让我们看一下接口层次结构:
- IEnumerable<T>允许您的类使用foreach枚举 - ICollection<T>是一个表示集合的IEnumerable<T>: - 它允许检索项目Count - 您可能可以从集合中Add/Remove/Clear项目 - 集合可能是只读的,在这种情况下,IsReadOnly应该返回true - 还有一些其他的辅助方法:Contains/CopyTo。 - IList<T>是一个允许按索引访问项目的ICollection<T>。 - 它添加了一个索引器 - 一些与索引相关的函数:Insert/RemoveAt - IndexOf 你应该实现哪个接口是一个语义问题:
- IEnumerable<T>只是一个可枚举的序列。消费代码只能枚举一次,因为您无法预知多次枚举的行为。像ReSharper这样的工具甚至会发出警告,如果您多次枚举IEnumerable<T>。 - 当然,大多数情况下,您可以安全地多次枚举它,但有时候不应该这样做。例如,枚举可能执行SQL查询(例如Linq-to-SQL)。
您通过定义一个函数GetEnumerator来实现IEnumerable<T>,该函数返回一个IEnumerator<T>。枚举器是一个指向您的序列中当前元素的指针。它可以返回此Current值,并且可以使用MoveNext移动到下一个元素。它也是可处理的(并且在foreach结束时被处理)。
让我们分解一下foreach循环:
IEnumerable<T> sequence = ... // Whatever
foreach (T item in sequence)
    DoSomething(item);

这等同于以下内容:

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迭代一个类的必要条件。这里足够使用鸭子类型(Duck typing),但我离题太远了。

当然,你可以很容易地使用yield关键字来实现该模式:

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

这将创建一个私有类,为您实现IEnumerable<int>,因此您不需要自己实现。 ICollection<T>表示集合,可以多次安全地枚举。但是你真的不知道它是什么样的集合。它可能是一组、列表、字典等等。
这就是集合语义。
一些例子:
- T[] - 即使您无法Add/Remove,它也实现了ICollection<T> - List<T> - HashSet<T> - 集合的很好的例子,但不是列表 - Dictionary<TKey, TValue> - 是的,那是一个ICollection<KeyValuePair<TKey, TValue>> - LinkedList<T> - ObservableCollection<T> IList<T>让您知道集合是一种可以轻松按索引访问元素(即在O(1)时间内)的集合。
对于您的循环链表来说并非如此,因为它不仅需要O(n)时间,而且首先没有有意义的索引。
一些例子:
- T[] - List<T> - ObservableCollection<T> 请注意,HashSet<T>Dictionary<TKey, TValue>不再在列表中。这些不是列表。LinkedList<T>在语义上是一个列表,但它不以O(1)时间提供按索引访问(需要O(n))。
我应该提到,在.NET 4.5中有只读的等效项:IReadOnlyCollection<out T>IReadOnlyList<out T>。这些很好,因为它们提供了协变性。

非常感谢您详尽的解释!现在我的想法更加清晰了。 - Mstislav Toivonen

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