Resharper建议在最底层的例子中使用顶部示例。但是我的印象是首先会创建一个新的项目列表,因此所有_executeFuncs都将在调用runstoredprocedure之前运行。
这通常不是问题,但是容易发生异常,如果我的假设是正确的,那么尽管已经运行了函数,我的数据库也不会更新?
foreach (var result in rows.Select(row => _executeFunc(row)))
{
RunStoredProcedure(result)
}
或者
foreach(var row in rows)
{
var result = _executeFunc(row);
RunStoredProcedure(result);
}
答案 0 :(得分:4)
在您的第一个示例中,在_executeFunc(row)
循环开始之前,rows
中的每个项目都不会首先调用foreach
。 LINQ将推迟执行。有关详细信息,请参阅This answer。
活动顺序如下:
rows
executeFunc(row)
RunStoredProcedure(result)
rows
现在,如果您的代码是这样的:
foreach (var result in rows.Select(row => _executeFunc(row)).ToList())
{
RunStoredProcedure(result)
}
然后它将首先为.Select
中的每个项运行LINQ rows
,因为.ToList()
会导致集合被枚举。
答案 1 :(得分:4)
在这种情况下,语句在语义上与Select
(和一般的linq)相同,使用委托的延迟执行。在结果生效之前,它不会运行任何已声明的查询,并且根据您编写该查询的方式,它将以适当的顺序执行。
一个非常简单的例子来说明:
var list = new List<string>{"hello", "world", "example"};
Func<string, string> func = (s) => {
Console.WriteLine(s);
return s.ToUpper();
};
foreach(var item in list.Select(i => func(i)))
{
Console.WriteLine(item);
}
结果
hello
HELLO
world
WORLD
example
EXAMPLE
答案 2 :(得分:3)
在上面的示例中,使用Select
将按行yielding
逐个投影行。
所以
foreach (var result in rows.Select(row => _executeFunc(row)))
与
基本相同 foreach(var row in rows)
因此Select正在做这样的事情
for each row in source
result = _executeFunc(row)
yield result
这个收益率是逐一传递每一行(它比这复杂一点,但这个解释现在应该足够了)。
如果你这样做了
foreach (var result in rows.Select(row => _executeFunc(row)).ToList())
调用ToList()
将立即返回行列表,这意味着在您有机会调用RunStoredProcedure()
之前,确实会为每一行调用_executeFunc()。
因此,Resharper所建议的是有效的。公平地说,我确信Jetbrains开发者知道他们在做什么:)
答案 3 :(得分:2)
Select
使用延迟执行。这意味着它将按顺序:
rows
_executeFunc
RunStoredProcedure
_executeFunc
然后它将对下一个项目执行相同操作,直到所有列表都已处理完毕。
答案 4 :(得分:1)
执行将被推迟意味着他们将具有相同的exec