Для LINQ к объектам, это компилируется в набор статических вызовов метода:
var productNames =
from p in products
where p.Id > 100 and p.Id < 5000
select p.ProductName;
становится:
IEnumerable<string> productNames = products
.Where(p => p.Id > 100 and p.Id < 5000)
.Select(p => p.ProductName);
Это использует методы расширения, определенные в Enumerable
типа, поэтому на самом деле составлен до:
IEnumerable<string> productNames =
Enumerable.Select(
Enumerable.Where(products, p => p.Id > 100 and p.Id < 5000),
p => p.ProductName
);
Лямбда-выражения для обработки этого метода превращаются в метод s компилятором. Лямбда в том, где превращается в метод, который может быть установлен на Func<Product, Boolean>
, и выбрать в Func<Product, String>
.
Для подробного объяснения см. Jon Skeet's blog series: Reimplementing LINQ to Objects.Он просматривает весь процесс, как это работает, включая преобразования компилятора (от синтаксиса запроса до вызовов метода), способы реализации и т. Д.
Обратите внимание, что реализации LINQ to Sql и IQueryable<T>
отличаются друг от друга. Expression<T>
, который генерируется лямбдой, передается провайдеру запросов, который, в свою очередь, каким-то образом (это зависит от поставщика, как это сделать) превращается в вызовы, обычно выполняемые на сервере в случае ORM ,
Для этого метода, например:
private static IEnumerable<string> ProductNames(IEnumerable<Product> products)
{
var productNames =
from p in products
where p.Id > 100 && p.Id < 5000
select p.ProductName;
return productNames;
}
компилируется к следующему IL:
.method private hidebysig static class [mscorlib]System.Collections.Generic.IEnumerable`1<string> ProductNames(class [mscorlib]System.Collections.Generic.IEnumerable`1<class ConsoleApplication3.Product> products) cil managed
{
.maxstack 3
.locals init (
[0] class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable,
[1] class [mscorlib]System.Collections.Generic.IEnumerable`1<string> enumerable2)
L_0000: nop
L_0001: ldarg.0
L_0002: ldsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, bool> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate3
L_0007: dup
L_0008: brtrue.s L_001d
L_000a: pop
L_000b: ldnull
L_000c: ldftn bool ConsoleApplication3.Program::<ProductNames>b__2(class ConsoleApplication3.Product)
L_0012: newobj instance void [mscorlib]System.Func`2<class ConsoleApplication3.Product, bool>::.ctor(object, native int)
L_0017: dup
L_0018: stsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, bool> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate3
L_001d: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<class ConsoleApplication3.Product>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, bool>)
L_0022: ldsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, string> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_0027: dup
L_0028: brtrue.s L_003d
L_002a: pop
L_002b: ldnull
L_002c: ldftn string ConsoleApplication3.Program::<ProductNames>b__4(class ConsoleApplication3.Product)
L_0032: newobj instance void [mscorlib]System.Func`2<class ConsoleApplication3.Product, string>::.ctor(object, native int)
L_0037: dup
L_0038: stsfld class [mscorlib]System.Func`2<class ConsoleApplication3.Product, string> ConsoleApplication3.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_003d: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!1> [System.Core]System.Linq.Enumerable::Select<class ConsoleApplication3.Product, string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [mscorlib]System.Func`2<!!0, !!1>)
L_0042: stloc.0
L_0043: ldloc.0
L_0044: stloc.1
L_0045: br.s L_0047
L_0047: ldloc.1
L_0048: ret
}
Обратите внимание, что это нормальные call
инструкции для вызовов методов. В лямбды преобразуются в другие методы, такие как:
[CompilerGenerated]
private static bool <ProductNames>b__2(Product p)
{
return ((p.Id > 100) && (p.Id < 0x1388));
}
LINQ to _what_? – SLaks
'запрос LINQ компилируется в лямбда с петлей внутри него. Затем это вызывается, когда переменная перечисляется в первый раз (после чего сохраняются результаты). Другой пользователь сказал, что LINQ выполняет дополнительные оптимизации, такие как хеширование. Большая часть ошибок неверна. – SLaks
[Reimplementing LINQ to Objects - Jon Skeet] (http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-1-introduction.aspx) – Habib