我对编译和优化LINQ表达式感兴趣,是否需要仔细考虑表达式中let和where子句的顺序。
以下是示例:
var query =
from record in Database.Table
let recordName = record.GetName()
let notUsed = UselessData()
let stuff = DoSomethingIntensiveWith(record)
where recordName == "foobar"
select stuff;
foreach (string item in query) {
Console.WriteLine("item => '{0}'", item);
}
问题/假设:
record.GetName()
才能执行where
子句。notUsed
从未在表达式中使用,因此UselessData()
将被调用
在所有?stuff
等于“foobar”时才需要recordName
。将
DoSomethingIntensiveWith()
执行每条记录或只记录
recordName
等于“foobar”?如果我想确保DoSomethingIntensiveWith()
仅在调用时调用
recordName
等于“foobar”,我是否需要在let
之后放置where
caluse
var query =
from record in Database.Table
let recordName = record.GetName()
let notUsed = UselessData()
where recordName == "foobar"
let stuff = DoSomethingIntensiveWith(record)
select stuff;
foreach (string item in query) {
Console.WriteLine("item => '{0}'", item);
}
条款,如下:
{{1}}
与此同时,我将使用一些真正的代码和调试器。生病 报告我发现的内容。
答案 0 :(得分:2)
如果这是LINQ-to-Objects,那么:是的,你这样做。标准Enumerable.*
实施在按顺序应用方面非常直接。您不一定需要所有let
条款,但事情仍按顺序完成,并且该顺序得到尊重。如果它是LINQ-to-anything-else,那么所有的赌注都会关闭。
这很容易证明:
using System;
using System.Linq;
class Foo
{
public Foo(string value)
{
Value = value;
}
public string Value { get; private set; }
public string Expensive()
{
Console.WriteLine(Value);
return Value;
}
static void Main()
{
var foos = new[] {
new Foo("abc"),
new Foo("def")};
Console.WriteLine("query1:");
var query1 = (from obj in foos
let val = obj.Value
where val.StartsWith("a")
let result = obj.Expensive()
select result).ToArray();
Console.WriteLine("query2:");
var query2 = (from obj in foos
let val = obj.Value
let result = obj.Expensive()
where val.StartsWith("a")
select result).ToArray();
}
}
第一个查询过滤然后项目(因此它只对匹配记录执行昂贵的操作),而第二个查询计算两者的昂贵操作:
query1:
abc
query2:
abc
def
应该注意let
实际上只是通过Select
实现的 - 它是从源数据到查询内部使用的匿名类型的投影。