2

我对编译和优化 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()被调用吗?
  • stuffrecordName仅当等于“foobar”时才需要。将为 DoSomethingIntensiveWith()每条记录执行还是仅对recordName等于“foobar”的记录执行?

如果我想确保DoSomethingIntensiveWith()只在 recordName等于“foobar”时调用,我是否需要将letcaluse放在 where子句之后,如下所示:

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);
}

与此同时,我将使用一些真实的代码和调试器。我会报告我的发现。

4

1 回答 1

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的——它是从源数据到查询内部使用的匿名类型的投影。

于 2012-11-05T11:59:28.523 回答