在LINQ查询中评估In-Clause的频率是多少?

时间:2016-01-24 11:22:42

标签: c# linq

我刚刚在another question上回答了以下LINQ查询,它返回了某些字符串组合的列表:

public static List<String> Combis(string value)
{   
  var combis =
    from bool1 in new bool[] {true, false}
    from bool2 in new bool[] {true, false}
    let i1 = 1
    let i2 = 1
    from i3 in new int[] {10, 20, 30}
    select value + "_" + bool1 + "_" + bool2 + "_" + i1 + "_" + i2 + "_" + i3;

  return combis.ToList();
}

我现在问自己:每个子句(例如,... in new bool[] {true, false})只被评估过一次或多次,即,每个子句中是否只有一个多个创建的数组?

更新

如答案所示,它被多次评估。我会将代码更改为

public static List<String> Combis(string value)
{
    bool[] bools = new[] {true, false};
    int[] ints = new[] {10, 20, 30};

    var combis =
        from bool1 in bools
        from bool2 in bools
        let i1 = 1
        let i2 = 1
        from i3 in ints
        select value + "_" + bool1 + "_" + bool2 + "_" + i1 + "_" + i2 + "_" + i3;

    return combis.ToList();
}

因此不必创建多个数组。

2 个答案:

答案 0 :(得分:1)

多次评估。查询表达式具有指定的方法调用转换。我使用Resharper来执行转换。避开你的眼睛......

new bool[] { true, false }
.SelectMany(bool1 => new bool[] { true, false }, (bool1, bool2) => new { bool1, bool2 })
.Select(@t => new { @t, i1 = 1 })
.Select(@t => new { @t, i2 = 1 })
.SelectMany(@t => new int[] { 10, 20, 30 }, (@t, i3) => "" + "_" + @t.@t.@t.bool1 + "_" + @t.@t.@t.bool2 + "_" + @t.@t.i1 + "_" + @t.i2 + "_" + i3);

对于每个bool1,正在创建一个新数组。

它真的不能是任何其他方式。如果您写了以下内容怎么办?

from bool1 in new [] { true, false }
from bool2 in new [] { bool1 } //dependent on bool1!

可以根据范围内的所有范围变量生成内部序列。这是动态的。

但是,C#编译器可以优化它吗?它不能,因为不允许知道Enumerable.SelectMany不依赖于传入的序列的对象标识。例如,它可以将每个哈希代码打印到控制台。

答案 1 :(得分:1)

可以按如下方式测试(LINQPad的代码):

void Main()
{
    Combis("");
}

private static bool[] Test(string arg)
{
    Console.WriteLine("!{0}", arg);
    return new bool[] {true, false};
}

public static List<String> Combis(string value)
{
  var combis =
    from bool1 in Test("A")
    from bool2 in Test("B")
    let i1 = 1
    let i2 = 1
    from i3 in new int[] {10, 20, 30}
    select value + "_" + bool1 + "_" + bool2 + "_" + i1 + "_" + i2 + "_" + i3;

  return combis.ToList();
}

输出:

!A
!B
!B

您可以看到Test("A")被调用一次,Test("B")被调用了两次。

我们也可以考虑int数组:

void Main()
{
    Combis("");
}

private static bool[] TestBool(string arg)
{
    Console.WriteLine("!{0}", arg);
    return new bool[] {true, false};
}

private static int[] TestInt(string arg)
{
    Console.WriteLine("!{0}", arg);
    return new int[] {10, 20, 30};
}

public static List<String> Combis(string value)
{
  var combis =
    from bool1 in TestBool("A")
    from bool2 in TestBool("B")
    let i1 = 1
    let i2 = 1
    from i3 in TestInt("C")
    select value + "_" + bool1 + "_" + bool2 + "_" + i1 + "_" + i2 + "_" + i3;

  return combis.ToList();
}

输出:

!A
!B
!C
!C
!B
!C
!C

我们发现每次调用TestInt("C")(其中有两个)后,TestBool("A")被调用两次,总共四次。

正确更新问题中显示的数组初始化将阻止双重初始化。