Generated IL Differences for VB.NET and C #

Today I played with the Entity Framework, and I read that the generated IL for C # was different from VB.NET for the following code:

VB.NET:

Dim ctx As New TravelEntities Sub Main() CallContext() CallContext() CallContext() End Sub Private Sub CallContext() Dim someCustomer = From x In ctx.Customer Where x.CustomerId.Equals(5) Select x Console.WriteLine(someCustomer.Count()) End Sub 

FROM#:

  private static TravelEntities ctx = new TravelEntities(); static void Main(string[] args) { CallContext(); CallContext(); CallContext(); } private static void CallContext() { var someCustomer = from x in ctx.Customer where x.CustomerId.Equals(5) select x; Console.WriteLine(someCustomer.Count()); } 

They produce the following IL:

VB:

 .method private static void CallContext() cil managed { // Code size 195 (0xc3) .maxstack 7 .locals init ([0] class [System.Core]System.Linq.IQueryable`1<class VB_IL_Difference.Customer> someCustomer, [1] class [System.Core]System.Linq.Expressions.ParameterExpression VB$t_ref$S0, [2] class [System.Core]System.Linq.Expressions.Expression[] VB$t_array$S0, [3] class [System.Core]System.Linq.Expressions.ParameterExpression[] VB$t_array$S1, [4] class [System.Core]System.Linq.Expressions.ParameterExpression VB$t_ref$S1, [5] class [System.Core]System.Linq.Expressions.ParameterExpression[] VB$t_array$S2) IL_0000: nop IL_0001: ldsfld class VB_IL_Difference.TravelEntities VB_IL_Difference.Module1::ctx IL_0006: callvirt instance class [System.Data.Entity]System.Data.Objects.ObjectSet`1<class VB_IL_Difference.Customer> VB_IL_Difference.TravelEntities::get_Customer() IL_000b: ldtoken VB_IL_Difference.Customer IL_0010: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0015: ldstr "x" IL_001a: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string) IL_001f: stloc.1 IL_0020: ldloc.1 IL_0021: ldtoken method instance int32 VB_IL_Difference.Customer::get_CustomerId() IL_0026: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle) IL_002b: castclass [mscorlib]System.Reflection.MethodInfo IL_0030: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo) IL_0035: ldtoken method instance bool [mscorlib]System.Int32::Equals(int32) IL_003a: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle) IL_003f: castclass [mscorlib]System.Reflection.MethodInfo IL_0044: ldc.i4.1 IL_0045: newarr [System.Core]System.Linq.Expressions.Expression IL_004a: stloc.2 IL_004b: ldloc.2 IL_004c: ldc.i4.0 IL_004d: ldc.i4.5 IL_004e: box [mscorlib]System.Int32 IL_0053: ldtoken [mscorlib]System.Int32 IL_0058: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_005d: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type) IL_0062: stelem.ref IL_0063: nop IL_0064: ldloc.2 IL_0065: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo, class [System.Core]System.Linq.Expressions.Expression[]) IL_006a: ldc.i4.1 IL_006b: newarr [System.Core]System.Linq.Expressions.ParameterExpression IL_0070: stloc.3 IL_0071: ldloc.3 IL_0072: ldc.i4.0 IL_0073: ldloc.1 IL_0074: stelem.ref IL_0075: nop IL_0076: ldloc.3 IL_0077: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class VB_IL_Difference.Customer,bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[]) IL_007c: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::Where<class VB_IL_Difference.Customer>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0,bool>>) IL_0081: ldtoken VB_IL_Difference.Customer IL_0086: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_008b: ldstr "x" IL_0090: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string) IL_0095: stloc.s VB$t_ref$S1 IL_0097: ldloc.s VB$t_ref$S1 IL_0099: ldc.i4.1 IL_009a: newarr [System.Core]System.Linq.Expressions.ParameterExpression IL_009f: stloc.s VB$t_array$S2 IL_00a1: ldloc.s VB$t_array$S2 IL_00a3: ldc.i4.0 IL_00a4: ldloc.s VB$t_ref$S1 IL_00a6: stelem.ref IL_00a7: nop IL_00a8: ldloc.s VB$t_array$S2 IL_00aa: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class VB_IL_Difference.Customer,class VB_IL_Difference.Customer>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[]) IL_00af: call class [System.Core]System.Linq.IQueryable`1<!!1> [System.Core]System.Linq.Queryable::Select<class VB_IL_Difference.Customer,class VB_IL_Difference.Customer>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0,!!1>>) IL_00b4: stloc.0 IL_00b5: ldloc.0 IL_00b6: call int32 [System.Core]System.Linq.Queryable::Count<class VB_IL_Difference.Customer>(class [System.Core]System.Linq.IQueryable`1<!!0>) IL_00bb: call void [mscorlib]System.Console::WriteLine(int32) IL_00c0: nop IL_00c1: nop IL_00c2: ret } // end of method Module1::CallContext 

FROM#:

  .method private hidebysig static void CallContext() cil managed { // Code size 141 (0x8d) .maxstack 7 .locals init ([0] class [System.Core]System.Linq.IQueryable`1<class C_IL_Difference.Customer> someCustomer, [1] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000, [2] class [System.Core]System.Linq.Expressions.Expression[] CS$0$0001, [3] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0002) IL_0000: nop IL_0001: ldsfld class C_IL_Difference.TravelEntities C_IL_Difference.Program::ctx IL_0006: callvirt instance class [System.Data.Entity]System.Data.Objects.ObjectSet`1<class C_IL_Difference.Customer> C_IL_Difference.TravelEntities::get_Customer() IL_000b: ldtoken C_IL_Difference.Customer IL_0010: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_0015: ldstr "x" IL_001a: call class [System.Core]System.Linq.Expressions.ParameterExpression [System.Core]System.Linq.Expressions.Expression::Parameter(class [mscorlib]System.Type, string) IL_001f: stloc.1 IL_0020: ldloc.1 IL_0021: ldtoken method instance int32 C_IL_Difference.Customer::get_CustomerId() IL_0026: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle) IL_002b: castclass [mscorlib]System.Reflection.MethodInfo IL_0030: call class [System.Core]System.Linq.Expressions.MemberExpression [System.Core]System.Linq.Expressions.Expression::Property(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo) IL_0035: ldtoken method instance bool [mscorlib]System.Int32::Equals(int32) IL_003a: call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle) IL_003f: castclass [mscorlib]System.Reflection.MethodInfo IL_0044: ldc.i4.1 IL_0045: newarr [System.Core]System.Linq.Expressions.Expression IL_004a: stloc.2 IL_004b: ldloc.2 IL_004c: ldc.i4.0 IL_004d: ldc.i4.5 IL_004e: box [mscorlib]System.Int32 IL_0053: ldtoken [mscorlib]System.Int32 IL_0058: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) IL_005d: call class [System.Core]System.Linq.Expressions.ConstantExpression [System.Core]System.Linq.Expressions.Expression::Constant(object, class [mscorlib]System.Type) IL_0062: stelem.ref IL_0063: ldloc.2 IL_0064: call class [System.Core]System.Linq.Expressions.MethodCallExpression [System.Core]System.Linq.Expressions.Expression::Call(class [System.Core]System.Linq.Expressions.Expression, class [mscorlib]System.Reflection.MethodInfo, class [System.Core]System.Linq.Expressions.Expression[]) IL_0069: ldc.i4.1 IL_006a: newarr [System.Core]System.Linq.Expressions.ParameterExpression IL_006f: stloc.3 IL_0070: ldloc.3 IL_0071: ldc.i4.0 IL_0072: ldloc.1 IL_0073: stelem.ref IL_0074: ldloc.3 IL_0075: call class [System.Core]System.Linq.Expressions.Expression`1<!!0> [System.Core]System.Linq.Expressions.Expression::Lambda<class [mscorlib]System.Func`2<class C_IL_Difference.Customer,bool>>(class [System.Core]System.Linq.Expressions.Expression, class [System.Core]System.Linq.Expressions.ParameterExpression[]) IL_007a: call class [System.Core]System.Linq.IQueryable`1<!!0> [System.Core]System.Linq.Queryable::Where<class C_IL_Difference.Customer>(class [System.Core]System.Linq.IQueryable`1<!!0>, class [System.Core]System.Linq.Expressions.Expression`1<class [mscorlib]System.Func`2<!!0,bool>>) IL_007f: stloc.0 IL_0080: ldloc.0 IL_0081: call int32 [System.Core]System.Linq.Queryable::Count<class C_IL_Difference.Customer>(class [System.Core]System.Linq.IQueryable`1<!!0>) IL_0086: call void [mscorlib]System.Console::WriteLine(int32) IL_008b: nop IL_008c: ret } // end of method Program::CallContext 

It appears that the version of this VB.NET code will contact the database every time the code is executed, and the C # version will retrieve objects from the cache when the code is executed several times.

Why do they make both languages ​​behave differently? It was my fallacy that both languages ​​simply differed in syntax and had almost exactly the same generated IL.

Are there any other examples where both languages ​​generate such different ILs?

+6
source share
2 answers

Parts of the differences that you see may arise from calling the end. Choose x. Since this is not required in the VB query syntax, but you explicitly declare it, VB includes it in compilation. You could just as easily formulate the VB syntax as follows:

 Dim someCustomer = From x In ctx.Customer Where x.CustomerId.Equals(5) 

Since C # requires a substantially missing Select clause at compile time, the compiler optimizes it in the generated IL.

I suspect that in this example you will see big differences between the generated expression trees between VB and C # if you used CustomerName = (=) "Foo" because C # and VB have very different string equality handling. I saw quite a few LINQ providers (including LINQ to Bing, LINQ to Twitter, EF Sample Query Provider, NOrm) that were unable to validate CustomerName = "Foo" in VB because they only tested their parsing of expressions in C #.

Regarding your claim that C # caches results, I don't see this using the following code for Northwind (using LinqPad). It still calls the database 3 times.

 void Main() { CallContext(); CallContext(); CallContext(); } private void CallContext() { var someCustomer = from x in Customers where x.CustomerID.Equals("ALFKI") select x; Console.WriteLine(someCustomer.Count()); } 
+5
source

Both codes will call the database each time the CallContext method is called. The only difference is in the expression tree generated for the LINQ expression, which in this case is not much different.

As you said in your comment that you now used the LINQ method chain, not the LINQ syntax, why the generated expression tree will now be the same.

+2
source

Source: https://habr.com/ru/post/895965/


All Articles