LINQ如何编译成CIL?
例如:
var query = from c in db.Cars select c; foreach(Car aCar in query) { Console.WriteLine(aCar.Name); }
一旦编译,这将如何翻译? 幕后发生了什么?
它按以下方式编译:
-
首先,将LINQ查询表达式转换为方法调用:
public static void Main() { var query = db.Cars.Select
(c => c); foreach (Car aCar in query) { Console.WriteLine(aCar.Name); } } -
如果
db.Cars
的类型为IEnumerable
(它用于LINQ-to-Objects),则lambda表达式将转换为单独的方法:private Car lambda0(Car c) { return c; } private Func
CachedAnonymousMethodDelegate1; public static void Main() { if (CachedAnonymousMethodDelegate1 == null) CachedAnonymousMethodDelegate1 = new Func (lambda0); var query = db.Cars.Select (CachedAnonymousMethodDelegate1); foreach // ... } 实际上,该方法不称为
lambda0
而是类似
(其中b__0 Main
是包含方法的名称)。 类似地,缓存的委托实际上称为CS$<>9__CachedAnonymousMethodDelegate1
。如果您使用的是LINQ-to-SQL,那么
db.Cars
的类型为IQueryable
,此步骤非常不同。 它会将lambda表达式转换为表达式树:public static void Main() { var parameter = Expression.Parameter(typeof(Car), "c"); var lambda = Expression.Lambda
>(parameter, new ParameterExpression[] { parameter })); var query = db.Cars.Select (lambda); foreach // ... } -
foreach
循环转换为try/finally
块(两者都是一样的):IEnumerator
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
:// 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
Program::CachedAnonymousMethodDelegate1 L_0021: brtrue.s L_0034 L_0023: ldnull L_0024: ldftn Car Program::lambda0(Car) L_002a: newobj instance void Func ::.ctor(object, native int) L_002f: stsfld Func Program::CachedAnonymousMethodDelegate1 // Put the delegate for “c => c” on the stack L_0034: ldsfld Func Program::CachedAnonymousMethodDelegate1 // Call to Enumerable.Select() L_0039: call IEnumerable Enumerable::Select (IEnumerable, Func) L_003e: stloc.1 // “try” block starts here L_003f: ldloc.1 L_0040: callvirt instance IEnumerator IEnumerable ::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 ::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
版本的编译代码也是预期的。 以下是与上述不同的重要部分(局部变量现在将具有不同的偏移和名称,但让我们忽略它):// 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 Expression::Lambda
>(Expression, ParameterExpression[]) // var query = Queryable.Select(...); L_004b: call IQueryable Queryable::Select (IQueryable, Expression >) L_0050: stloc.1
您应该编译它并对生成的可执行文件运行ildasm
以查找。
上述就是C#学习教程:LINQ如何编译成CIL?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/998837.html