对我而言,C#中的IEnumerable<T>
(嗯,.NET)表示可以迭代的任意数据集。它可以由任何东西支持,例如SELECT
查询的结果,数组的内容,用户在控制台中键入的字符或Pi的数字。索引不能引用数据,它不一定是有限的或无限的,也不能被修改。它甚至可以是稍后同样调用的不同数据集,如随机数的IEnumerable<double>
。它只是为消费者提供了一些数据,如foreach
循环。
现在考虑一个处理数据集的其他概念:SQL。在SQL中,除非明确指定,否则行的顺序不保证且不相关。例如,如果您执行SELECT * FROM stack_overflow_posts LIMIT 1
,则数据库没有暗示您获取的行实际上是插入的第一行,也不是最旧的行。例如,您需要使用ORDER BY posted_date_time
明确地对结果进行排序。
这个相同的概念是否适用于IEnumerable<T>
的.NET中的枚举? 使用IEnumerable<T>
是否意味着结果将始终按特定顺序产生?在我之前给出的示例中,我会说是,订单会被隐含,因为如果它们是以不同的顺序列举,结果将毫无意义;如果您以不同于实际击键的顺序获得用户在控制台中键入的字符,那么阅读它们的重点是什么?显然,LINQ有OrderBy()
可以根据需要订购结果,但这是明确的排序,而不是隐含的。
实现接口的类实际上有希望遵循特定模式,而不仅仅是实现接口定义的方法。 IEnumerable<T>
是否意味着其数据将以相关的顺序产生,或者如果他们希望这样做,是否由明确订购的消费者决定?如果我有一个以未定义的顺序生成项目的方法 - 或者更确切地说,一个与消费者无关并且随时可能发生变化的订单 - 我应该使用IEnumerable<T>
之外的其他内容吗?
答案 0 :(得分:10)
使用
IEnumerable<T>
是否意味着结果将始终按特定顺序产生?
没有。 IEnumerable
只保证对象可以迭代。例如,List<T>
始终根据索引按升序输出项目这一事实是List<T>
具体的实现细节。
实现接口的类实际上有希望遵循特定模式,而不仅仅是实现接口定义的方法。
IEnumerable<T>
是否意味着其数据将以相关的顺序产生,或者如果他们希望这样做,是否要由枚举的消费者明确排序?
不,实施IEnumerable
并不意味着任何订单。在使用IEnumerable
的对象时,如果您希望保证每次都以相同的顺序输出数据,则必须明确提供订单。
如果您考虑实现IEnumerable
的CLR集合类型,这很简单。想象一下,您创建了一个返回IEnumerable<string>
的方法。该方法可以返回List<string>
,其IEnumerable
的实现具有一定的顺序,但它可以很容易地返回HashSet<string>
,订单没有意义。
如果我有一个方法以未定义的顺序生成项目 - 或者更确切地说,一个与消费者无关且可能随时更改的订单 - 我应该使用
IEnumerable<T>
之外的其他内容吗?
我说IEnumerable<T>
很适合你的需求。为了更加清楚,您可以记录您的方法并声明结果中的项目顺序是未定义的,并且可能会因呼叫而改变。
答案 1 :(得分:7)
IEnumerable<T>
不保证订单,但实施者或返回IEnumerable<T>
的方法可以保证订单。例如,File.ReadLines
保证按顺序为您提供文件行。 Enumerable.Where
保证保留订单。但是,正如您所提到的,您可以轻松编写一个每次都产生新GUID的方法,而且它没有顺序。
答案 2 :(得分:1)
IEnumerable<T>
的一个不幸的限制是,虽然大多数实现都有许多特征,而一些消费者依赖这些特征,但是没有任何方法可以指示对象是否具有这样的特征。
在这些特征中,如果一个方法接收IEnumerable<T>
调用GetEnumerator
并迭代集合的内容,它将在有限数量{{1}之后到达终点。调用;如果它再次调用MoveNext
而没有对传入的GetEnumerator
做任何其他事情,它应该以相同的顺序接收相同的项目序列并在同一点结束。
请注意,上述内容实际上包含许多不连续的标准;有许多实现满足所有要求,但实现几乎可以满足任何组合。在上述声明的不同标准中:
枚举将产生有限数量的项目。
多个枚举将始终产生相同数量的项目。
一次迭代中出现的所有项目都会以相同的顺序出现在另一次迭代中。
如果枚举没有产生有限数量的项目,那么多次迭代是否产生相同数量的问题就没有实际意义。然而,除此之外,IEnumerable<T>
的实施可以遵守上述标准的其余六种组合中的任何一种:
许多集合,当仅由一个线程使用时,当然会遵守这三个集合。
A&#34; true&#34;随机生成器可能不遵守任何。
伪随机生成器可能只遵守#3。
并发只添加列表可能会遵守#1和#3。
其他一些并发集合可能只遵守#1。
并发代码使用的数组可能只遵循#1和#2。
虽然接收IEnumerable<T>
的某些方法如果传递的对象不能遵守上述部分或全部特征可能会出现故障,但这些特征不属于IEnumerable<T>
合同的一部分,也没有IEnumerable<T>
指定可以满足的标准的方式。期望似乎是将IEnumerable<T>
传递给方法的代码负责知道&#34; kind&#34;它具有IEnumerable<T>
,并且将从外部代码接收的IEnumerable<T>
传递给方法的方法应该以某种方式向其消费者传达IEnumerable<T>
实例传递到的方法的要求。
如果IEnumerable<T>
被枚举一次,它将按某种顺序返回项目。对于大多数IEnumerable<T>
实现,重复的枚举将以相同的顺序生成项目,并且某些代码依赖于该行为,但IEnumerable<T>
合同中没有任何内容指定。。< / p>