订单相关性是否隐含在IEnumerable<;T>;或者应该是明确的

本文关键字:或者 gt lt 是否 相关性 IEnumerable | 更新日期: 2023-09-27 18:26:09

对我来说,C#(好吧,.NET)中的IEnumerable<T>表示可以迭代的任意数据集。它可以由任何东西支持,比如SELECT查询的结果、数组的内容、用户在控制台中键入的字符或Pi的数字。数据不能被索引引用,它不一定是有限的或无限的,也不能被修改。当以后调用相同时,它甚至可能是一组不同的数据,比如随机数的IEnumerable<double>。它只是像foreach循环一样向消费者提供的一些数据。

现在考虑一下其他处理数据集的概念:SQL。在SQL中,除非明确指定,否则不能保证行的顺序,也不相关。例如,如果您执行SELECT * FROM stack_overflow_posts LIMIT 1,则数据库不会暗示您返回的行实际上是插入的第一行,也不是最旧的行。例如,您需要使用ORDER BY posted_date_time对结果进行显式排序。

同样的概念适用于.NET中带有IEnumerable<T>的枚举吗使用IEnumerable<T>是否意味着结果总是按特定顺序产生在我前面给出的例子中,我会说是的,顺序是隐含的,因为如果按不同的顺序枚举,结果将毫无意义;如果用户在控制台中键入的字符与实际的击键顺序不同,那么读取它们有什么意义?很明显,LINQ有OrderBy()可以根据您的需要对结果进行排序,但这是显式排序,而不是隐式排序。

实现接口的类有效地保证遵循特定的模式,而不仅仅是实现接口定义的方法。IEnumerable<T>是否意味着其数据将按相关顺序生成,或者如果枚举的消费者希望这样做,则由他们明确地进行排序?如果我有一个方法以未定义的顺序生成项目——或者更确切地说,是一个与消费者无关且随时可能更改的订单——我应该使用IEnumerable<T>以外的东西吗?

订单相关性是否隐含在IEnumerable<;T>;或者应该是明确的

IEnumerable<T>的使用是否意味着结果总是以特定的顺序产生?

没有。IEnumerable只是保证对象可以被迭代。例如,List<T>总是根据索引按升序输出项目,这是List<T>的具体实现细节。

实现接口的类有效地保证遵循特定的模式,而不仅仅是实现接口定义的方法。IEnumerable<T>是否意味着其数据将按相关顺序生成,或者如果枚举的消费者希望这样做,则由他们明确排序?

不,实现IEnumerable并不意味着任何顺序。当使用IEnumerable的对象时,如果要保证数据每次都以相同的顺序出现,则必须显式提供顺序。

如果您想到实现IEnumerable的CLR集合类型,这很简单。假设您创建了一个返回IEnumerable<string>的方法。该方法可以返回一个List<string>,其IEnumerable的实现确实有特定的顺序,但它也可以很容易地返回HashSet<string>,对于它来说,顺序是没有意义的。

如果我有一个方法以未定义的顺序生成项目——或者更确切地说,是一个与消费者无关且随时可能更改的订单——我应该使用IEnumerable<T>以外的东西吗?

我认为IEnumerable<T>非常适合您的需求。为了更加清楚,您可以记录您的方法,并声明结果中项目的顺序是未定义的,并且可以在不同的调用之间更改。

IEnumerable<T>不保证订单,但实现者,或者返回IEnumerable<T>的方法可以保证订单。例如,File.ReadLines保证按顺序提供文件行。Enumerable.Where保证保持顺序。然而,正如您所提到的,您可以很容易地编写一个方法,每次都会产生一个新的GUID,而且它没有顺序。

IEnumerable<T>的一个不幸限制是,尽管大多数实现都具有许多特性,而且一些消费者也依赖这些特性,但对象无法指示它们是否具有这些特性。

在这些特征中,如果接收IEnumerable<T>的方法调用GetEnumerator并遍历集合的内容,则它将在有限数量的MoveNext调用后到达末尾;如果它再次调用GetEnumerator而没有对传入的IEnumerable<T>执行任何其他操作,那么它应该以相同的顺序接收相同的项目序列并在相同的点结束。

请注意,上述内容实际上包含了许多离散的标准;有许多实现可以满足所有要求,但实现几乎可以满足任何组合。在上述声明的不同标准中:

  1. 枚举将产生有限数量的项。

  2. 多个枚举总是会产生相同数量的项。

  3. 在一次迭代中出现的所有项目都将以相同的顺序出现在另一次迭代。

如果枚举没有产生有限数量的项,那么多次迭代是否产生相同数量的问题就没有意义了。然而,除此之外,IEnumerable<T>的实现可以遵守上述标准的六个剩余组合中的任何一个:

  • 许多集合,当只由一个线程使用时,当然会遵守所有三个线程。

  • 一个"真正的"随机生成器可能不会遵守任何规则。

  • 伪随机生成器可能仅遵守#3。

  • 并发的仅添加列表可能遵循#1和#3。

  • 其他一些并发集合可能只遵守#1。

  • 并发代码使用的数组可能只遵守#1和#2。

尽管如果传递的对象未能遵守上述某些或全部特征,接收IEnumerable<T>的一些方法可能会出现故障,但这些特征不是IEnumerable<T>契约的一部分,IEnumerable<T>也没有任何方法来指定它可以满足什么标准。我们的期望似乎是,将IEnumerable<T>传递给方法的代码负责知道它有什么"类型"的IEnumerable<T>,而将从外部代码接收到的IEnumerable<T>传递给方法,方法应该以某种方式向其消费者传达IEnumerable<T>实例所传递的方法的要求。

如果一个IEnumerable<T>被枚举一次,它将以某种顺序返回项。对于大多数IEnumerable<T>实现,重复的枚举将产生相同序列中的项,并且一些代码依赖于这种行为,但IEnumerable<T>契约中没有指定这一点

相关文章: