我需要在自己的类中保持集合语义。你能解释一下什么是集合语义吗?据我理解,它是一组必须在类中实现的接口。这是真的吗?如果是,那么我需要在类中实现什么以及为什么?这两个接口——ICollection和IEnumerable——足够了吗,还是只有它们是最必要的?
我正在使用这篇文章来帮助编写循环链表。
我正在使用这篇文章来帮助编写循环链表。
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)时间内)的集合。T[]
- List<T>
- ObservableCollection<T>
请注意,HashSet<T>
和Dictionary<TKey, TValue>
不再在列表中。这些不是列表。LinkedList<T>
在语义上是一个列表,但它不以O(1)时间提供按索引访问(需要O(n))。IReadOnlyCollection<out T>
,IReadOnlyList<out T>
。这些很好,因为它们提供了协变性。