首先,LINQ查询表达式被转换为方法调用:
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<Car>
(这在LINQ-to-Objects中是这样的),那么lambda表达式会被转换为一个单独的方法: 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
,而是类似于<Main>b__0
的名称(其中Main
是包含该方法的名称)。同样地,缓存的委托实际上被称为CS$<>9__CachedAnonymousMethodDelegate1
。
如果您使用的是LINQ-to-SQL,则db.Cars
将是IQueryable<Car>
类型,此步骤非常不同。它会将 lambda 表达式转换为表达式树:
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
循环会被转换为一个try/finally
块(两者相同):
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<Car>
的内容:
// 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
IQueryable<Car>
版本的编译代码也符合预期。以下是与上述内容不同的重要部分(现在本地变量将具有不同的偏移量和名称,但让我们忽略这一点):
// 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