LINQ如何编译到CIL? [英] How is LINQ compiled into the CIL?
问题描述
例如:
var query = from c in db.Cars select c;
foreach(Car aCar in query)
{
Console.WriteLine(aCar.Name);
}
这将如何转化一旦被编译?在幕后会发生什么?
How would this translate once it is compiled? What happens behind the scenes?
推荐答案
有以下述方式编译
-
首先,LINQ查询前pression转化为方法调用:
First, the LINQ query expression is transformed into method calls:
public static void Main()
{
var query = db.Cars.Select<Car, Car>(c => c);
foreach (Car aCar in query)
{
Console.WriteLine(aCar.Name);
}
}
如果 db.Cars
的类型的IEnumerable&LT;汽车&GT;
(它是LINQ-到对象),然后将拉姆达前pression变成一个单独的方法:
If db.Cars
is of type IEnumerable<Car>
(which it is for LINQ-to-Objects), then the lambda expression is turned into a separate method:
private Car lambda0(Car c)
{
return c;
}
private Func<Car, Car> CachedAnonymousMethodDelegate1;
public static void Main()
{
if (CachedAnonymousMethodDelegate1 == null)
CachedAnonymousMethodDelegate1 = new Func<Car, Car>(lambda0);
var query = db.Cars.Select<Car, Car>(CachedAnonymousMethodDelegate1);
foreach // ...
}
在现实中的方法不叫 lambda0
但类似&lt;主&GT; b__0
(其中主
是包含方法的名称)。同样,缓存的委托实际上是一个名为 CS $&LT;方式&gt; 9__CachedAnonymousMethodDelegate1
In reality the method is not called lambda0
but something like <Main>b__0
(where Main
is the name of the containing method). Similarly, the cached delegate is actually called CS$<>9__CachedAnonymousMethodDelegate1
.
如果您正在使用LINQ到SQL,那么 db.Cars
将类型的IQueryable&LT;汽车&GT;
这一步是非常不同的。它会转而用在lambda前pression到前pression树:
If you are using LINQ-to-SQL, then db.Cars
will be of type IQueryable<Car>
and this step is very different. It would instead turn the lambda expression into an expression tree:
public static void Main()
{
var parameter = Expression.Parameter(typeof(Car), "c");
var lambda = Expression.Lambda<Func<Car, Car>>(parameter, new ParameterExpression[] { parameter }));
var query = db.Cars.Select<Car, Car>(lambda);
foreach // ...
}
的的foreach
循环转化为尝试/终于
块(这是上是相同的):
The foreach
loop is transformed into a try/finally
block (this is the same for both):
IEnumerator<Car> enumerator = null;
try
{
enumerator = query.GetEnumerator();
Car aCar;
while (enumerator.MoveNext())
{
aCar = enumerator.Current;
Console.WriteLine(aCar.Name);
}
}
finally
{
if (enumerator != null)
((IDisposable)enumerator).Dispose();
}
最后,被编译成IL预期的方式。以下是的IEnumerable&LT;汽车&GT;
:
// Put db.Cars on the stack
L_0016: ldloc.0
L_0017: callvirt instance !0 DatabaseContext::get_Cars()
// "if" starts here
L_001c: ldsfld Func<Car, Car> Program::CachedAnonymousMethodDelegate1
L_0021: brtrue.s L_0034
L_0023: ldnull
L_0024: ldftn Car Program::lambda0(Car)
L_002a: newobj instance void Func<Car, Car>::.ctor(object, native int)
L_002f: stsfld Func<Car, Car> Program::CachedAnonymousMethodDelegate1
// Put the delegate for "c => c" on the stack
L_0034: ldsfld Func<Car, Car> Program::CachedAnonymousMethodDelegate1
// Call to Enumerable.Select()
L_0039: call IEnumerable<!!1> Enumerable::Select<Car, Car>(IEnumerable<!!0>, Func<!!0, !!1>)
L_003e: stloc.1
// "try" block starts here
L_003f: ldloc.1
L_0040: callvirt instance IEnumerator<!0> IEnumerable<Car>::GetEnumerator()
L_0045: stloc.3
// "while" inside try block starts here
L_0046: br.s L_005a
L_0048: ldloc.3 // body of while starts here
L_0049: callvirt instance !0 IEnumerator<Car>::get_Current()
L_004e: stloc.2
L_004f: ldloc.2
L_0050: ldfld string Car::Name
L_0055: call void Console::WriteLine(string)
L_005a: ldloc.3 // while condition starts here
L_005b: callvirt instance bool IEnumerator::MoveNext()
L_0060: brtrue.s L_0048 // end of while
L_0062: leave.s L_006e // end of try
// "finally" block starts here
L_0064: ldloc.3
L_0065: brfalse.s L_006d
L_0067: ldloc.3
L_0068: callvirt instance void IDisposable::Dispose()
L_006d: endfinally
编译code为的IQueryable&LT;汽车&GT;
版本也如预期。下面是从上面的不同(局部变量都会有不同的偏移和名称现在,但是让我们无视)的重要组成部分:
The compiled code for the IQueryable<Car>
version is also as expected. Here is the important part that is different from the above (the local variables will have different offsets and names now, but let’s disregard that):
// typeof(Car)
L_0021: ldtoken Car
L_0026: call Type Type::GetTypeFromHandle(RuntimeTypeHandle)
// Expression.Parameter(typeof(Car), "c")
L_002b: ldstr "c"
L_0030: call ParameterExpression Expression::Parameter(Type, string)
L_0035: stloc.3
// Expression.Lambda(...)
L_0036: ldloc.3
L_0037: ldc.i4.1 // var paramArray = new ParameterExpression[1]
L_0038: newarr ParameterExpression
L_003d: stloc.s paramArray
L_003f: ldloc.s paramArray
L_0041: ldc.i4.0 // paramArray[0] = parameter;
L_0042: ldloc.3
L_0043: stelem.ref
L_0044: ldloc.s paramArray
L_0046: call Expression<!!0> Expression::Lambda<Func<Car, Car>>(Expression, ParameterExpression[])
// var query = Queryable.Select(...);
L_004b: call IQueryable<!!1> Queryable::Select<Car, Car>(IQueryable<!!0>, Expression<Func<!!0, !!1>>)
L_0050: stloc.1
这篇关于LINQ如何编译到CIL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!