与未存储的列表或数组一起使用时,foreach 循环是否工作得更慢?

Ali*_*Tor 1 c# arrays performance foreach loops

我想知道foreach如果将未存储的列表或数组用作in数组或List.

我的意思是这样的:

foreach (int number in list.OrderBy(x => x.Value)
 {
  // DoSomething();
 }
Run Code Online (Sandbox Code Playgroud)

此代码中的循环是否计算每次迭代的排序?

使用存储值的循环:

 List<Tour> list = tours.OrderBy(x => x.Value) as List<Tour>;
    foreach (int number in list)
      {
       // DoSomething();
      }
Run Code Online (Sandbox Code Playgroud)

如果是,哪个代码显示更好的性能,存储值与否?

Joe*_*orn 5

这通常是违反直觉的,但一般来说,最有利于性能的选项是尽可能长时间地等待,以将结果具体化为一个具体的结构,如列表或数组。请记住,这是一个概括,因此在很多情况下它不成立。然而,当您尽可能避免创建列表时,第一直觉会更好。

为了用您的示例进行演示,我们有以下两个选项:

var list = tours.OrderBy(x => x.Value).ToList();
foreach (int number in list)
{
   // DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

与此选项:

foreach (int number in list.OrderBy(x => x.Value))
{
     // DoSomething();
}
Run Code Online (Sandbox Code Playgroud)

要了解这里发生了什么,您需要查看.OrderBy()扩展方法。阅读链接的文档,您会看到它返回一个IOrderedEnumerable<TSource>对象。使用 IOrderedEnumerable,foreach当您第一次开始迭代对象时,循环所需的所有排序都已经完成(我相信这是您问题的关键:不,它不会在每次迭代时重新排序)。另请注意,两个示例都使用相同的OrderBy()调用。因此,这两个样本在排序结果时要解决相同的问题,并且它们以相同的方式完成它,这意味着它们在代码中达到该点所需的时间完全相同。

那么,代码示例中的区别完全在于foreach直接使用循环与第一次调用.ToList(),因为在这两种情况下我们都是从IOrderedEnumerable. 让我们仔细看看这些差异。

当你打电话时.ToList(),你认为会发生什么?这种方法并不神奇。这里仍然有代码必须执行才能生成列表。这段代码仍然有效地使用它自己的foreach循环,你看不到. 此外,一旦您只需要担心一次处理一个对象的 RAM 是否足够,您现在就要强迫您的程序分配一个新的 RAM 块,该块大到足以容纳整个集合的引用。超越引用,您可能还需要为完整对象创建新的内存分配,如果您之前从流或数据库读取器中读取 a,那么一次实际上只需要 RAM 中的一个对象。这在内存是主要约束的系统上尤其重要,Web 服务器通常就是这种情况,您可能会为许多会话提供和维护会话 RAM,但每个会话只是偶尔使用任何 CPU 时间来请求一个新的一页。

现在我在这里做出一个假设,即您正在处理尚未列出的内容。我的意思是,前几段谈到需要将 IOrderedEnumerable 转换为 List,而不是将 List 转换为某种形式的 IEnumerable。我需要承认,在创建和操作 .Net 用来实现这些对象的状态机时有一些小的开销。不过,我认为这是一个很好的假设。事实证明,它比我们意识到的要多得多。即使在这个问题的示例中,我们也通过调用OrderBy()函数的简单虚拟来支付这笔费用。

总之,使用原始 IEnumerable 与转换为列表可能会有一些额外的开销,但可能没有。此外,您几乎肯定会通过尽可能避免转换为 List 来为自己节省一些 RAM……可能会占用大量RAM。