Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generated IL differences for VB.NET and C#

Today I was playing around with Entity Framework and I've read that the generated IL for C# was different than 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

C#:

    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

C#:

    .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

As it seems the VB.NET version of this code will contact the database every time the code is executed while the C# version will retrieve the entities from the cache when the code is executed multiple times.

Why would they make both languages behave in such a different manner? It was my misconception that both languages just differed in syntax and had almost exactly the same generated IL.

Are there any more examples where both languages generated such different IL?

like image 585
Luc Bos Avatar asked Aug 26 '11 11:08

Luc Bos


People also ask

What is the difference between the environment of the Visual Basic to the C#?

C# is case sensitive while Visual Basic . NET is not. Thus in C# it is possible to have two variables with the same apparent name, for example variable1 and Variable1 .

Which is faster VB.NET or C#?

It is that VB computes the loop bound once while C# computes the loop condition on each iteration. It is just a fundamental difference in the way the languages were intended to be used. ...is slightly faster than the equivalent in VB.

Is VB.NET still used 2021?

It is a fact that coding languages never die. Even the oldest coding languages such as Classic Asp, VB6, COBOL, FORTRAN, VB.Net, etc. are still in use in few organizations.


2 Answers

Part of the differences you are seeing may be due to calling the end Select x. Since it is not required in the VB query syntax but you are explicitly declaring it, VB includes it in the compilation. You could have stated the VB syntax as follows just as easily:

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

Since C# requires the essentially no-oped Select clause at compile time, the compiler optimizes it out in the generated IL.

I suspect in this example, you would see greater differences between the generated expression trees between VB and C# if you used CustomerName =(=) "Foo" because C# and VB have very different handling of string equality. I've seen quite a few LINQ providers (including LINQ to Bing, LINQ to Twitter, EF Sample Query Provider, NOrm) that fail to evaute CustomerName = "Foo" in VB because they only tested their expression tree parsing in C#.

As for your claim that C# caches the results, I'm not seeing that using the following code against Northwind (using LinqPad). It is still calling 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());          
} 
like image 131
Jim Wooley Avatar answered Oct 01 '22 08:10

Jim Wooley


Both the code will call the database each time the CallContext method is called. The difference just in the expression tree generated for the LINQ expression, which in this case is not much of a difference.

As you said in your comment that now you have used LINQ method chaining rather than LINQ syntax, that's why now the generated expression tree will be same.

like image 28
Ankur Avatar answered Oct 01 '22 08:10

Ankur