Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>It is compiled in the following way:</p> <ol> <li><p>First, the LINQ query expression is transformed into method calls:</p> <pre><code>public static void Main() { var query = db.Cars.Select&lt;Car, Car&gt;(c =&gt; c); foreach (Car aCar in query) { Console.WriteLine(aCar.Name); } } </code></pre></li> <li><p>If <code>db.Cars</code> is of type <code>IEnumerable&lt;Car&gt;</code> (which it is for LINQ-to-Objects), then the lambda expression is turned into a separate method:</p> <pre><code>private Car lambda0(Car c) { return c; } private Func&lt;Car, Car&gt; CachedAnonymousMethodDelegate1; public static void Main() { if (CachedAnonymousMethodDelegate1 == null) CachedAnonymousMethodDelegate1 = new Func&lt;Car, Car&gt;(lambda0); var query = db.Cars.Select&lt;Car, Car&gt;(CachedAnonymousMethodDelegate1); foreach // ... } </code></pre> <p>In reality the method is not called <code>lambda0</code> but something like <code>&lt;Main&gt;b__0</code> (where <code>Main</code> is the name of the containing method). Similarly, the cached delegate is actually called <code>CS$&lt;&gt;9__CachedAnonymousMethodDelegate1</code>.</p> <p>If you are using LINQ-to-SQL, then <code>db.Cars</code> will be of type <code>IQueryable&lt;Car&gt;</code> and this step is very different. It would instead turn the lambda expression into an expression tree:</p> <pre><code>public static void Main() { var parameter = Expression.Parameter(typeof(Car), "c"); var lambda = Expression.Lambda&lt;Func&lt;Car, Car&gt;&gt;(parameter, new ParameterExpression[] { parameter })); var query = db.Cars.Select&lt;Car, Car&gt;(lambda); foreach // ... } </code></pre></li> <li><p>The <code>foreach</code> loop is transformed into a <code>try/finally</code> block (this is the same for both):</p> <pre><code>IEnumerator&lt;Car&gt; 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(); } </code></pre></li> <li><p>Finally, this is compiled into IL the expected way. The following is for <code>IEnumerable&lt;Car&gt;</code>:</p> <pre><code>// 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&lt;Car, Car&gt; Program::CachedAnonymousMethodDelegate1 L_0021: brtrue.s L_0034 L_0023: ldnull L_0024: ldftn Car Program::lambda0(Car) L_002a: newobj instance void Func&lt;Car, Car&gt;::.ctor(object, native int) L_002f: stsfld Func&lt;Car, Car&gt; Program::CachedAnonymousMethodDelegate1 // Put the delegate for “c =&gt; c” on the stack L_0034: ldsfld Func&lt;Car, Car&gt; Program::CachedAnonymousMethodDelegate1 // Call to Enumerable.Select() L_0039: call IEnumerable&lt;!!1&gt; Enumerable::Select&lt;Car, Car&gt;(IEnumerable&lt;!!0&gt;, Func&lt;!!0, !!1&gt;) L_003e: stloc.1 // “try” block starts here L_003f: ldloc.1 L_0040: callvirt instance IEnumerator&lt;!0&gt; IEnumerable&lt;Car&gt;::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&lt;Car&gt;::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></pre> <p>The compiled code for the <code>IQueryable&lt;Car&gt;</code> 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):</p> <pre><code>// 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&lt;!!0&gt; Expression::Lambda&lt;Func&lt;Car, Car&gt;&gt;(Expression, ParameterExpression[]) // var query = Queryable.Select(...); L_004b: call IQueryable&lt;!!1&gt; Queryable::Select&lt;Car, Car&gt;(IQueryable&lt;!!0&gt;, Expression&lt;Func&lt;!!0, !!1&gt;&gt;) L_0050: stloc.1 </code></pre></li> </ol>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload