【问题标题】:What is collection semantics (in .NET)?什么是集合语义(在 .NET 中)?
【发布时间】:2014-11-29 20:59:51
【问题描述】:

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

我正在使用this article 作为帮助来编写一个循环链表。

【问题讨论】:

  • 链接的文章已经实现了ICollection。
  • 是的,我知道。这让我有点困惑。 @HenkHolterman

标签: c# .net collections interface semantics


【解决方案1】:

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

您通过定义一个函数来实现IEnumerable<T>GetEnumerator,它返回 en 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 一起使用。鸭子打字在这里就足够了,但我离题太多了。

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

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

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

ICollection&lt;T&gt; 表示一个集合,可以安全地多次枚举。但是你真的不知道它是什么样的收藏品。它可以是一个集合、一个列表、一个字典等等。

这是集合语义。

一些例子:

  • T[] - 它实现了ICollection&lt;T&gt;,即使你不能Add/Remove
  • List&lt;T&gt;
  • HashSet&lt;T&gt; - 集合但不是列表的一个很好的例子
  • Dictionary&lt;TKey, TValue&gt; - 是的,那是ICollection&lt;KeyValuePair&lt;TKey, TValue&gt;&gt;
  • LinkedList&lt;T&gt;
  • ObservableCollection&lt;T&gt;

IList&lt;T&gt; 让您知道该集合是可以让您通过索引轻松访问元素的类型(即 O(1) 时间)。 p>

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

一些例子:

  • T[]
  • List&lt;T&gt;
  • ObservableCollection&lt;T&gt;

请注意 HashSet&lt;T&gt;Dictionary&lt;TKey, TValue&gt; 不再在列表中。这些不是列表。 LinkedList&lt;T&gt; 在语义上是一个列表,但它不提供 O(1) 时间内的索引访问(它需要 O(n))。

我应该提一下 .NET 4.5 中的只读等效项:IReadOnlyCollection&lt;out T&gt;IReadOnlyList&lt;out T&gt;。它们提供的协方差很好。

【讨论】:

  • 非常感谢您详尽的解释!现在我的想法更清晰了。
猜你喜欢
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-04
相关资源
最近更新 更多