我第一次使用.NET中的Entity Framework工作,并且一直在编写LINQ查询以便从我的模型中获取信息.我想从一开始就养成良好的习惯,所以我一直在研究编写这些查询的最佳方法,并得到他们的结果.不幸的是,在浏览Stack Exchange时,我似乎遇到了延迟/立即执行如何与LINQ一起使用的两个相互矛盾的解释:
在LINQ查询中显示有问题的慢foreach() - ToList()极大地提升了性能 - 为什么会这样?,暗示需要调用"ToList()"以便立即评估查询,因为foreach正在重复评估数据源上的查询,从而大大减慢了操作.
另一个例子是问题通过分组linq结果进行预测非常缓慢,任何提示?,其中接受的答案还暗示在查询上调用"ToList()"将提高性能.
显示有问题foreach只执行一次查询吗?,暗示foreach导致建立一个枚举,并且每次都不会查询数据源.
继续浏览网站已经出现了许多问题,其中"在foreach循环中重复执行"是性能问题的罪魁祸首,还有很多其他答案表明foreach将从数据源中适当地获取单个查询,这意味着两者都是解释似乎有效.如果"ToList()"假设不正确(因为美国东部时间2013-06-05 1:51 PM的大部分当前答案似乎都暗示),这种误解来自何处?这些解释中是否有一个是准确的,哪个不是,或者是否存在可能导致LINQ查询以不同方式进行评估的不同情况?
编辑:除了下面接受的答案之外,我对程序员提出了以下问题,这非常有助于我对查询执行的理解,特别是在循环期间可能导致多个数据源命中的陷阱,我认为对这个问题感兴趣的人有用:https://softwareengineering.stackexchange.com/questions/178218/for-vs-foreach-vs-linq
eva*_*nal 18
通常,LINQ使用延迟执行.如果您使用类似的方法First(),FirstOrDefault()则立即执行查询.当你做某事时;
foreach(string s in MyObjects.Select(x => x.AStringProp))
Run Code Online (Sandbox Code Playgroud)
结果以流式方式检索,意味着一个接一个.每次迭代器调用时MoveNext,投影都会应用于下一个对象.如果你有一个Where它将首先应用过滤器,然后投影.
如果你做的事情;
List<string> names = People.Select(x => x.Name).ToList();
foreach (string name in names)
Run Code Online (Sandbox Code Playgroud)
然后我相信这是一个浪费的操作.ToList()将强制执行查询,枚举People列表并应用x => x.Name投影.之后,您将再次枚举列表.因此,除非你有充分的理由将数据放在列表中(而不是IEnumerale),否则你只是在浪费CPU周期.
一般来说,对于使用foreach枚举的集合使用LINQ查询,其性能不会比任何其他类似和实用的选项差.
另外值得注意的是,鼓励实现LINQ提供程序的人员使常用方法像在Microsoft提供的提供程序中那样工作,但他们并不需要这样做.如果我要将LINQ写入HTML或LINQ to My Proprietary Data Format提供程序,则无法保证它以这种方式运行.也许数据的性质会使立即执行成为唯一可行的选择.
另外,最后编辑; 如果您对此感兴趣,Jon Skeet的C#In Depth内容非常丰富且非常精彩.我的回答总结了本书的几页(希望具有合理的准确性),但如果您想了解LINQ如何在幕后工作的更多细节,那么这是一个值得关注的好地方.
在LinqPad上尝试这个
void Main()
{
var testList = Enumerable.Range(1,10);
var query = testList.Where(x =>
{
Console.WriteLine(string.Format("Doing where on {0}", x));
return x % 2 == 0;
});
Console.WriteLine("First foreach starting");
foreach(var i in query)
{
Console.WriteLine(string.Format("Foreached where on {0}", i));
}
Console.WriteLine("First foreach ending");
Console.WriteLine("Second foreach starting");
foreach(var i in query)
{
Console.WriteLine(string.Format("Foreached where on {0} for the second time.", i));
}
Console.WriteLine("Second foreach ending");
}
Run Code Online (Sandbox Code Playgroud)
每次运行where委托时,我们都会看到一个控制台输出,因此我们可以看到每次都运行Linq查询.现在通过查看控制台输出,我们看到第二个foreach循环仍然导致"在哪里打印"打印,从而表明foreach的第二次使用实际上导致where子句再次运行...可能导致速度减慢.
First foreach starting
Doing where on 1
Doing where on 2
Foreached where on 2
Doing where on 3
Doing where on 4
Foreached where on 4
Doing where on 5
Doing where on 6
Foreached where on 6
Doing where on 7
Doing where on 8
Foreached where on 8
Doing where on 9
Doing where on 10
Foreached where on 10
First foreach ending
Second foreach starting
Doing where on 1
Doing where on 2
Foreached where on 2 for the second time.
Doing where on 3
Doing where on 4
Foreached where on 4 for the second time.
Doing where on 5
Doing where on 6
Foreached where on 6 for the second time.
Doing where on 7
Doing where on 8
Foreached where on 8 for the second time.
Doing where on 9
Doing where on 10
Foreached where on 10 for the second time.
Second foreach ending
Run Code Online (Sandbox Code Playgroud)
这取决于如何使用Linq查询。
var q = {some linq query here}
while (true)
{
foreach(var item in q)
{
...
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码将多次执行Linq查询。不是因为foreach,而是因为foreach在另一个循环内,所以foreach本身被多次执行。
如果linq查询的所有使用者都“谨慎地”使用它并避免出现诸如上面的嵌套循环之类的愚蠢错误,则linq查询不应不必要地执行多次。
在某些情况下,有必要使用ToList()将linq查询简化为内存中的结果集,但我认为ToList()的使用频率非常高。每当涉及大数据时,ToList()几乎总是变成毒药,因为即使最外面的使用者/枚举器只需要10行,它也会将整个结果集(可能有数百万行)拉入内存并进行缓存。除非您有非常具体的理由并且您知道数据永远不会很大,否则请避免使用ToList()。
| 归档时间: |
|
| 查看次数: |
11004 次 |
| 最近记录: |