LINQ如何在内部工作?

How LINQ works internally?

我喜欢在.NET上使用Linq,但我想知道它在内部是如何工作的?

有人知道吗?

THKS。


询问LINQ的一个特定方面更有意义。这有点像问"Windows是如何工作"的。

从C的角度来看,Linq的关键部分是对我而言的:

  • 表达式树。这些是代码作为数据的表示。例如,表达式树可以表示"接受一个字符串参数,对其调用length属性,并返回结果"的概念。事实上,这些作为数据而不是编译代码存在意味着LINQ提供者(如LINQ to SQL)可以分析它们并将它们转换为SQL。
  • lambda表达式。这些表达式如下:

    1
    2
    3
    x => x * 2
    (int x, int y) => x * y
    () => { Console.WriteLine("Block"); Console.WriteLine("Lambda"); }

    lambda表达式被转换为委托树或表达式树。

  • 匿名类型。这些表达式如下:

    1
    new { X=10, Y=20 }

    这些仍然是静态类型的,只是编译器用属性XY为您生成了一个不可变的类型。这些通常与var一起使用,它允许从初始化表达式推断局部变量的类型。

  • 查询表达式。这些表达式如下:

    1
    2
    3
    from person in people
    where person.Age < 18
    select person.Name

    这些代码由C编译器翻译成"普通"的C.0(即不使用查询表达式的表单)。然后应用重载解决方案等,这对于能够在多个数据类型中使用相同的查询语法是绝对关键的,而不需要编译器了解诸如query able之类的类型。上述表达式将转换为:

    1
    2
    people.Where(person => person.Age < 18)
          .Select(person => person.Name)

  • 扩展方法。这些是静态方法,可以将其用作第一个参数类型的实例方法。例如,这样的扩展方法:

    1
    2
    3
    4
    public static int CountAsciiDigits(this string text)
    {
        return text.Count(letter => letter >= '0' && letter <= '9');
    }

    然后可以这样使用:

    1
    2
    string foo ="123abc456";
    int count = foo.CountAsciiDigits();

    注意,CountAsciiDigits的实现使用了另一种扩展方法,Enumerable.Count()

  • 小精灵

    这是大多数相关的语言方面。然后是标准查询运算符的实现,在Linq提供程序中,如Linq to Objects和Linq to SQL等。我有一个关于如何实现Linq to Objects相当简单的演示—它位于C深入网站的"Talks"页面上。

    像linq to sql这样的提供者通常是通过Queryable类工作的。在其核心,它们将表达式树转换为其他查询格式,然后用执行那些进程外查询的结果构造适当的对象。

    这包括你所感兴趣的一切吗?如果你还有什么特别想知道的,编辑一下你的问题,我就去试试。


    LINQ基本上是C 3.0离散特性的组合:

    • 局部变量类型推理
    • 自动属性(未在VB 9.0中实现)
    • 扩展方法
    • lambda表达式
    • 匿名类型初始值设定项
    • 查询理解
    • 小精灵

      有关到那里的旅程(Linq)的更多信息,请参见2008年Langnet上Anders的视频:

      http://download.microsoft.com/download/c/e/5/ce5434ca-4f54-42b1-81ea-7f5a72f3b1dd/1-01%20-%20csharp3%20-%20anders%20hejlsberg.wmv


      在简单的表单中,编译器接受代码查询,并将其转换为一组通用类和调用。在下面,对于linq2sql,使用dbcommand、dbdatareader等构造和执行动态SQL查询。

      假设你有:

      1
      var q = from x in dc.mytable select x;

      它被转换成以下代码:

      1
      2
      3
      4
      5
      6
      7
      IQueryable<tbl_dir_office> q =
          dc.mytable.Select<tbl_dir_office, tbl_dir_office>(
              Expression.Lambda<Func<mytable, mytable>>(
                  exp = Expression.Parameter(typeof(mytable),"x"),
                  new ParameterExpression[] { exp }
              )
          );

      大量的仿制药,巨大的开销。


      基本上,Linq是一些语言工具(编译器)和一些框架扩展的混合体。因此,当您编写LINQ查询时,它们将使用适当的接口(如iquerable)来执行。还要注意,运行时在LINQ中没有角色。

      但很难用一个简短的回答来公正对待林肯。我建议你读些书,让自己融入其中。我不确定这本书是否告诉了你Linq的内部原理,但是Linq在实际中给出了一个很好的解释。


      我有一个小的C程序,演示了C中LINQ的实现。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      class Program
      {
          static void Main(string[] args)
          {
              //Eventhough we call the method here, it gets called ONLY when the for loop is executed
              var Cities = LinQFunction(new List<string>() {"Bangalore","Mysore","Coorg","Tumkur","Kerala","TamilNadu" });

              //LinQFunction() gets callled now
              foreach(var city in Cities)
              {
                  Console.WriteLine(city);
              }
          }

         //This function is called ONLY when the foreach loop iterates and gets the item from the collection
         static IEnumerable<string> LinQFunction(List<string> cities)
          {
              foreach (var item in cities)
              {
                  //Return each 'item' at a time
                  yield return item;
              }
          }
      }

      使用适当的断点。