我花了一秒钟看看为什么我的应用程序表现糟糕。我所做的只是暂停调试器两次,我找到了它。
为什么每次都运行我的代码有一个实际的原因吗?我知道防止这种情况的唯一方法是在最后添加ToArray()。我想我需要修改所有代码并确保它们返回数组?
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
答案 0 :(得分:4)
Enumerables懒惰地评估(仅在需要时)。在select之后添加.ToList(),它将强制进行评估。
答案 1 :(得分:3)
选择会导致迭代器被迭代。
如果构建结果的成本很高,你可以.ToList()一次结果,然后继续使用该列表。
List<int> resultAsList = ls.ToList();
// Use resultAsList in each of the foreach statements
答案 2 :(得分:3)
LINQ有懒惰的评估方法,Select
就是其中之一。
问题是你正在使用foreach
两次,它会打印两次值。
答案 3 :(得分:1)
构建查询时
IEnumerable<int> ls = test.Select(s => { if (expensivePrint) { Console.WriteLine("Doing expensive math"); } return s.Length; });
它实际上并不执行并缓存结果,因为您显然期待。这被称为“违背执行”。
它只是构建查询。在查询上调用foreach
语句时,实际执行查询。
如果您在查询中致电ToList()
或ToArray()
或Sum()
或Average()
或此类运营商,则会立即执行此操作。
如果要保留查询结果,最好的办法是通过调用ToList()
或ToArray()
将其缓存在数组或列表中,并枚举此列表或数组而不是而不是构造的查询。
答案 4 :(得分:1)
这是Linq的延期执行。如果您需要简明扼要的完整解释,请阅读:
答案 5 :(得分:1)
此方法通过使用延迟执行来实现。立即返回值是一个对象,它存储执行操作所需的所有信息。在通过直接调用其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);
答案 6 :(得分:0)
我建议您使用.ToArray()
,return int[]
将为您提供更好的效果
int[]
的原因,因为它将立即声明和创建,以及List<T>
将在运行时逐个创建
int[] array = test.Select(s =>
{
if (expensivePrint)
{
Console.WriteLine("Doing expensive math");
}
return s.Length;
}).ToArray();