For example:
var query = from c in db.Cars select c;
foreach(Car aCar in query)
{
Console.WriteLine(aCar.Name);
}
How would this trans
You should compile it and run ildasm against the resulting executable to find out.
It is compiled in the following way:
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);
}
}
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 // ...
}
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
.
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 // ...
}
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();
}
Finally, this is compiled into IL the expected way. The following is for 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
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