【问题标题】:Is order relevance implied with IEnumerable<T> or should it be explicit?IEnumerable<T> 是否暗示了订单相关性,还是应该明确?
【发布时间】:2014-10-10 19:41:57
【问题描述】:

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

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

同样的概念是否适用于 .NET 中带有IEnumerable&lt;T&gt; 的枚举? IEnumerable&lt;T&gt; 的使用是否暗示结果将始终按特定顺序产生?在我之前给出的示例中,我会说是的,顺序将被暗示,因为如果它们被列举在不同的顺序,结果将毫无意义;如果您在控制台中以与实际击键不同的顺序获取用户键入的字符,那么阅读它们有什么意义?显然 LINQ 有 OrderBy() 可以根据需要对结果进行排序,但这是显式排序,而不是隐式排序。

实现接口的类实际上承诺遵循特定模式,而不仅仅是实现接口定义的方法。 IEnumerable&lt;T&gt; 是否暗示其数据将按相关顺序产生,或者是否由枚举的消费者明确排序(如果他们愿意)?如果我有一种方法可以以未定义的顺序生成商品——或者更确切地说,是与消费者无关且随时可能更改的订单——我应该使用IEnumerable&lt;T&gt;以外的其他东西吗?

【问题讨论】:

    标签: c# .net linq ienumerable


    【解决方案1】:

    IEnumerable&lt;T&gt; 的使用是否暗示结果将始终按特定顺序产生?

    没有。 IEnumerable 只是保证对象可以被迭代。例如,List&lt;T&gt; 始终根据索引以升序输出项目这一事实是 List&lt;T&gt; 的具体实现细节。

    实现接口的类实际上承诺遵循特定模式,而不仅仅是实现接口定义的方法。 IEnumerable&lt;T&gt; 是否暗示其数据将按相关顺序生成,或者是否由枚举的消费者明确排序(如果他们愿意)?

    不,实现IEnumerable 并不意味着任何顺序。在使用 IEnumerable 的对象时,如果您想保证您的数据每次都以相同的顺序输出,则必须明确提供顺序。

    如果您想到实现IEnumerable 的CLR 集合类型,这很简单。假设您创建了一个返回 IEnumerable&lt;string&gt; 的方法。该方法可以返回一个List&lt;string&gt;,它对IEnumerable 的实现确实 对它有一定的顺序,但它也可以很容易地返回一个HashSet&lt;string&gt;,因为它没有顺序感觉。

    如果我有一种方法可以以未定义的顺序生成商品——或者更确切地说,是与消费者无关且随时可能更改的订单——我是否应该使用 IEnumerable&lt;T&gt; 以外的其他方式?

    我会说IEnumerable&lt;T&gt; 非常适合您的需求。更清楚地说,您可以记录您的方法并声明结果中项目的顺序是未定义的,并且可能会因调用而异。

    【讨论】:

    【解决方案2】:

    IEnumerable&lt;T&gt; 不保证顺序,但实现者,或者返回IEnumerable&lt;T&gt; 的方法可以保证顺序。例如,File.ReadLines 保证按顺序为您提供文件行。 Enumerable.Where 保证保留订单。但是,正如您所提到的,您可以轻松编写一个每次产生一个新 GUID 的方法,并且它没有顺序。

    【讨论】:

    • +1,我喜欢说明方法 returning IEnumerable&lt;T&gt; 实际上可以指定返回的可枚举对象有某种顺序
    【解决方案3】:

    IEnumerable&lt;T&gt; 的一个不幸的限制是,虽然大多数实现都具有许多特性,并且一些消费者依赖于这些特性,但对象无法指示它们是否具有这些特性。

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

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

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

    2. 多个枚举将始终产生相同数量的项目。

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

    如果枚举不能产生有限数量的项目,那么多次迭代是否产生相同数量的问题是没有实际意义的。然而,除此之外,IEnumerable&lt;T&gt; 的实现可能会遵守上述标准的其余六种组合中的任何一种:

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

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

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

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

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

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

    虽然如果传递的对象未能遵守上述部分或全部特征,则接收IEnumerable&lt;T&gt; 的某些方法可能会发生故障,但这些特征不是IEnumerable&lt;T&gt; 合同的一部分,也没有任何方法可以使用@ 987654330@ 指定它可以满足哪些标准。期望似乎是将 IEnumerable&lt;T&gt; 传递给方法的代码负责了解它具有的 IEnumerable&lt;T&gt; 的“种类”,以及将从外部代码收到的 IEnumerable&lt;T&gt; 传递给方法的方法应该以某种方式向他们的消费者传达 IEnumerable&lt;T&gt; 实例传递到的方法的要求。

    如果一个IEnumerable&lt;T&gt; 被枚举一次,它将以某种顺序返回项目。对于大多数IEnumerable&lt;T&gt; 实现,重复枚举将产生相同顺序的项目,并且一些代码依赖于该行为,但IEnumerable&lt;T&gt; 合同中没有任何内容指定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      • 2021-12-12
      • 2011-03-11
      • 1970-01-01
      • 2012-09-24
      相关资源
      最近更新 更多