什么是集合语义(在.NET中)?
我需要在自己的类中维护集合语义。 难道你不解释,什么是集合语义? 据我所知,它是一组必须在类中实现的一些接口。 这是真的吗? 如果是 – 我必须在课堂上实现什么 ? 为什么 ? 这两个接口–ICollection和IEnumerable – 是否足够,或者这些只是最必要的接口?
我正在编写一个循环链表,使用这篇文章作为帮助。
.NET中有许多集合类型,它们都有一些共同的行为,例如:
- 您可以使用
foreach
枚举它们 - 他们有一个
Count
属性 - 您可以使用
Add
方法添加项目 - 等等…
这种行为是从集合类型中得到的,你猜对了:它都在ICollection
接口中。 我们来看看接口层次结构:
-
IEnumerable
允许使用foreach
枚举您的类 -
ICollection
是表示集合的IEnumerable
:- 它允许检索项目
Count
- 您可以
Add
/Remove
/Clear
集合中的项目 - 集合只能是只读的,在这种情况下,
IsReadOnly
应该返回true
- 还有其他一些辅助方法:
Contains
/CopyTo
。
- 它允许检索项目
-
IList
是ICollection
,允许通过索引访问项目。- 它添加了一个索引器
- 一些与索引相关的函数:
Insert
/RemoveAt
-
IndexOf
您应该实现哪个接口是语义问题 :
IEnumerable
只是一个可枚举的序列 。 它只应通过使用代码进行一次枚举,因为您永远不知道它在多个枚举中的行为方式。 像ReSharper这样的工具甚至会在多次枚举IEnumerable
发出警告。
当然,大多数时候你可以安全地多次枚举它,但有时你不应该。 例如,枚举可以执行SQL查询(例如,想想Linq-to-SQL)。
您可以通过定义一个函数来实现IEnumerable
: GetEnumerator
,它返回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
请注意,例如, HashSet
和Dictionary
不再位于列表中。 这些不是列表。 LinkedList
在语义上是一个列表,但它在O(1)时间内不提供索引访问(它需要O(n) )。
我应该提一下,在.NET 4.5中有只读的等价物: IReadOnlyCollection
, IReadOnlyList
。 这些对他们提供的协方差很有帮助。