0

我花了一秒钟来看看为什么我的应用程序性能很差。我所做的只是暂停调试器两次,然后我找到了它。

它每次都运行我的代码有实际原因吗?我知道防止这种情况的唯一方法是在最后添加 ToArray() 。我想我需要修改所有代码并确保它们返回数组?

在线演示http://ideone.com/EUfJN

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Test
{

    static void Main()
    {
        string[] test = new string[] { "a", "sdj", "bb", "d444"};
        var expensivePrint = false;
        IEnumerable<int> ls = test.Select(s => { if (expensivePrint) { Console.WriteLine("Doing expensive math"); } return s.Length; });
        expensivePrint = true;
        foreach (var v in ls)
        {
            Console.WriteLine(v);
        }
        Console.WriteLine("If you dont think it does it everytime, lets try it again");
        foreach (var v in ls)
        {
            Console.WriteLine(v);
        }
    }
}

输出

Doing expensive math
1
Doing expensive math
3
Doing expensive math
2
Doing expensive math
4
If you dont think it does it everytime, lets try it again
Doing expensive math
1
Doing expensive math
3
Doing expensive math
2
Doing expensive math
4
4

7 回答 7

4

Enumerables 懒惰地评估(仅在需要时)。在选择之后添加一个 .ToList() ,它将强制评估。

于 2012-07-25T22:11:38.673 回答
3

LINQ 有惰性求值方法,Select它就是其中之一。

问题是您使用foreach了两次,它会打印两次值。

于 2012-07-25T22:13:50.887 回答
3

Select导致迭代器被...迭代。

如果构建结果的成本很高,您可以 .ToList() 结果一次,然后继续使用该列表。

List<int> resultAsList = ls.ToList();
// Use resultAsList in each of the foreach statements
于 2012-07-25T22:11:42.613 回答
1

请参考文档Enumerable.Select

该方法是通过使用延迟执行来实现的。立即返回值是一个存储执行操作所需的所有信息的对象。在通过直接调用其 GetEnumerator 方法或使用 Visual C# 中的 foreach 或 Visual Basic 中的 For Each 枚举对象之前,不会执行此方法表示的查询。

通过迭代Select方法的结果,执行查询。 foreach是迭代该结果的一种方法。 ToArray是另一个。


它每次都运行我的代码有实际原因吗?

是的,如果结果没有被延迟,那么将执行比必要更多的迭代:

IEnumerable<string> query = Enumerable.Range(0, 100000)
  .Select(x => x.ToString())
  .Where(s => s.Length == 6)
  .Take(5);
于 2012-07-26T00:02:45.277 回答
1

构建查询时

IEnumerable<int> ls = test.Select(s => { if (expensivePrint) { Console.WriteLine("Doing expensive math"); } return s.Length; });

它实际上并没有像您显然期望的那样执行和缓存结果。这称为“延迟执行”。

它只是构建查询。查询的执行实际上发生在foreach对查询调用语句时。

如果您在查询中调用ToList()or ToArray()or Sum()or Average()or 任何类型的运算符,它将立即执行它。

如果要保留查询结果,最好的办法是通过调用ToList()or将其缓存在数组或列表中,ToArray()并在此列表或数组上而不是在构造的查询上枚举。

于 2012-07-25T22:16:33.467 回答
1

这是 Linq 的延迟执行。如果您需要简洁而完整的解释,请阅读以下内容:

http://weblogs.asp.net/dixin/archive/2010/03/16/understanding-linq-to-objects-6-deferred-execution.aspx

于 2012-07-25T22:16:38.057 回答
0

我建议您使用.ToArray()which willreturn int[]为您提供更好的性能

原因是int[]因为它会立即声明和创建,并且List<T>会在运行时一个一个地创建

int[] array = test.Select(s =>
{
   if (expensivePrint)
   {
     Console.WriteLine("Doing expensive math");
   }
   return s.Length;
}).ToArray();
于 2012-07-25T22:31:02.700 回答