C# LINQ序列-它们在IL中是如何链接的?

C# LINQ序列-它们在IL中是如何链接的?,c#,linq,C#,Linq,无论我使用表达式查询语法还是方法语法,IL看起来几乎相同 以“渐进式语法”执行LINQ查询: IEnumerable<Employee> query1 = _employees.Where(e => e.Location.Equals("California")); IEnumerable<Employee> query2 = query1.OrderByDescending(e => e.Name); IEnumerable<string> qu

无论我使用表达式查询语法还是方法语法,IL看起来几乎相同

以“渐进式语法”执行LINQ查询:

IEnumerable<Employee> query1 = _employees.Where(e => e.Location.Equals("California"));
IEnumerable<Employee> query2 = query1.OrderByDescending(e => e.Name);
IEnumerable<string> query3 = query2.Select(e => e.Name);
如果我以“流畅”的方式执行LINQ查询:

IEnumerable<string> names = _employees.Where(e => e.Location.Equals("California")).OrderByDescending(e => e.Name).Select(e => e.Name);
IEnumerable Name=\u employees.Where(e=>e.Location.Equals(“加利福尼亚”)).OrderByDescending(e=>e.Name)。选择(e=>e.Name);

我没有得到stloc.0、ldloc.0指令。这是“渐进语法”和“流畅语法”之间的唯一区别。JIT编译器中是否还有第二步生成额外的IL指令来进行序列输入?

两个版本之间的唯一区别是局部变量。在第一个版本中,每个调用的返回值都保存到本地,然后对该本地调用下一个方法,依此类推

我想你要问的是,在没有当地人的情况下,它是如何工作的?方法的返回值存储在哪里

它存储在计算堆栈中。然后使用求值堆栈中的值调用下一个方法。在链中调用最后一个方法后,结果被设置回局部变量
names

查看说明,逐步了解其工作原理:

  • 方法参数arg1到argN被推送到堆栈上

  • 方法参数arg1到argN从堆栈中弹出;这个 方法调用是使用这些参数执行的,控件是 传输到方法描述符引用的方法。什么时候 完成后,被调用方方法将生成并发送一个返回值 给打电话的人

  • 返回值被推送到堆栈上

  • 因此,在调用方法之前,将其参数推送到堆栈上。在您的第一个代码中,它是这样的:

  • \u employees
    推到堆栈上,将
    Func
    推到堆栈上
  • 调用
    Where
  • 将结果存储在
    本地0
  • 加载
    local 0
    (将其推到堆栈上),将
    Func
    推到堆栈上
  • 调用
    OrderByDescending
  • 将结果存储在
    本地1
  • 加载
    local 1
    ,将
    Func
    推到堆栈上
  • 调用
    选择
  • 将结果存储在
    local2
  • 第二个版本中没有load local stuff,因为方法调用的返回值已经存储在堆栈中。调用方法时,返回值将成为下一次调用的第一个参数,只有作为第二个参数的委托被推送到堆栈上,然后调用下一个方法

    我还应该提到,这并不是LINQ所特有的。方法链接就是这样工作的,LINQ并不特别


    注意:我简化了推送委托步骤,额外的代码用于缓存。在创建新的委托实例之前,委托实例缓存在编译器生成的类字段中。编译器会检查是否在创建委托实例之前进行了效率检查。

    在“渐进语法”和“流畅语法”之间的唯一区别是,在第一种情况下,您有一堆局部变量
    stloc.0
    ldloc.0
    用于写入和读取它们。查看特定行:IL_004a:call class[System.Core]System.Linq.iorderenumerable
    1[System.Core]System.Linq.Enumerable::OrderByDescending(class[mscorlib]System.Collections.Generic.IEnumerable
    1,class[mscorlib]System.Func`2)第一个参数是输入序列(Where操作的输出序列),它是什么!!0? 我感觉这就是实际的集合。@Alex.A我想它们指的是泛型类型参数。所以在这个调用中:
    System.Linq.Enumerable::Select
    !!0
    表示
    员工
    !!1
    表示
    字符串
    。你说得对。我只是想确定表示输入序列的IL代码的确切部分。是IL_0035:pop吗?@Alex.A否,输入序列在调用
    后被推送到堆栈中,没有单独的指令将其推送到堆栈中。
    的结果,其中
    成为
    OrderByDescending
    @Alex的输入。A从IL_0053开始直到调用指令(IL_0072)用于缓存的代码,我在注释中解释了它。它所做的只是将第二个参数(委托)推送到堆栈中。
    IL_002b: stloc.0      // query1
    
    // [100 13 - 100 82]
    IL_002c: ldloc.0      // query1
    
    IEnumerable<string> names = _employees.Where(e => e.Location.Equals("California")).OrderByDescending(e => e.Name).Select(e => e.Name);