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

C# 当与未存储的列表或数组一起使用时,foreach循环是否工作得更慢?,c#,arrays,performance,loops,foreach,C#,Arrays,Performance,Loops,Foreach,我想知道,如果在数组或列表中使用未存储的列表或数组作为,则foreach循环是否运行缓慢 我的意思是: foreach (int number in list.OrderBy(x => x.Value) { // DoSomething(); } 这段代码中的循环是否每次迭代都计算排序 使用存储值的循环: List<Tour> list = tours.OrderBy(x => x.Value) as List<Tour>; foreach

我想知道,如果在
数组或
列表中使用未存储的列表或数组作为
,则
foreach
循环是否运行缓慢

我的意思是:

foreach (int number in list.OrderBy(x => x.Value)
 {
  // DoSomething();
 }
这段代码中的循环是否每次迭代都计算排序

使用存储值的循环:

 List<Tour> list = tours.OrderBy(x => x.Value) as List<Tour>;
    foreach (int number in list)
      {
       // DoSomething();
      }
List List=tours.OrderBy(x=>x.Value)作为列表;
foreach(列表中的整数)
{
//DoSomething();
}
如果是,存储值与否,哪种代码显示了更好的性能?

此行不会运行:

List<Tour> list = tours.OrderBy(x => x.Value) as List<Tour>; // Returns null.
List List=tours.OrderBy(x=>x.Value)作为列表;//返回null。
相反,您希望以以下方式存储结果:

List<Tour> list = tours.OrderBy(x => x.Value).ToList();
List List=tours.OrderBy(x=>x.Value.ToList();
是的,第二个选项(存储结果)将更快地枚举,因为它将跳过排序操作。

此行不会运行:

List<Tour> list = tours.OrderBy(x => x.Value) as List<Tour>; // Returns null.
List List=tours.OrderBy(x=>x.Value)作为列表;//返回null。
相反,您希望以以下方式存储结果:

List<Tour> list = tours.OrderBy(x => x.Value).ToList();
List List=tours.OrderBy(x=>x.Value.ToList();
是的,第二个选项(存储结果)将更快地枚举,因为它将跳过排序操作。

是和否

是的,foreach语句似乎工作较慢

不,您的程序有相同的总工作量,因此您将无法测量与外部的差异

您需要关注的是,在没有.ToList或ToArray的情况下,不要多次使用惰性操作(在本例中是OrderBy)。在这种情况下,您只使用一次(foreach),但很容易错过

编辑:我只是想澄清一下。问题中的as语句将无法按预期工作,但我的答案假定OrderBy之后是no.ToList()。

是和否

是的,foreach语句似乎工作较慢

不,您的程序有相同的总工作量,因此您将无法测量与外部的差异

您需要关注的是,在没有.ToList或ToArray的情况下,不要多次使用惰性操作(在本例中是OrderBy)。在这种情况下,您只使用一次(foreach),但很容易错过


编辑:我只是想澄清一下。问题中的as语句不会按预期工作,但我的答案假设OrderBy之后没有.ToList()。

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

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

var list = tours.OrderBy(x => x.Value).ToList();
foreach (int number in list)
{
   // DoSomething();
}
与此选项相比:

foreach (int number in list.OrderBy(x => x.Value))
{
     // DoSomething();
}
要理解这里发生了什么,您需要查看扩展方法。阅读链接的文档,您将看到它返回一个
IOrderedEnumerable
对象。对于IOrderedEnumerable,当您第一次开始迭代对象时,
foreach
循环所需的所有排序都已经完成(我相信,这是您问题的关键:不,它不会在每次迭代时重新排序)。还要注意,两个示例使用相同的
OrderBy()
调用。因此,两个示例都有相同的问题需要解决,以便对结果进行排序,并且它们以相同的方式完成排序,这意味着它们在代码中花费的时间完全相同

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

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

现在我在这里做一个假设,你正在处理一些还没有列出的东西。我的意思是,前面几段讨论了需要将IOrderedEnumerable转换为列表,而不是将列表转换为某种形式的IEnumerable。我需要承认,在创建和操作.Net用来实现这些对象的状态机时,会有一些小的开销。然而,我认为这是一个很好的假设。事实证明,事实往往比我们意识到的要真实得多。即使在这个问题的示例中,我们也会通过调用
OrderBy()


总之,使用原始IEnumerable与转换为列表相比,可能会有一些额外的开销,但可能没有。此外,只要有可能,您几乎可以通过避免转换到列表来节省一些RAM。。。潜在的大量内存。

这通常是违反直觉的,但一般来说,对性能最好的选择是尽可能长时间地等待结果的实现