2011-08-26 12 views
6

Dziś Grałem około z Entity Framework i czytałem, że wygenerowany IL dla C# był inny niż VB.NET za pomocą następującego kodu:Wygenerowane różnice IL dla VB.NET i C#

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()); 
    } 

produkują IL następujące:

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 

Jak się wydaje wersję VB.NET tego kodu będzie się z bazą danych za każdym razem, gdy kod jest wykonywany podczas gdy wersja C# będzie odzyskać podmiotów z pamięci podręcznej, gdy kod jest wykonywany wiele razy.

Dlaczego sprawiają, że oba języki zachowują się w tak różny sposób? To było moje błędne przekonanie, że oba języki różniły się tylko składnią i miały prawie dokładnie taką samą wygenerowaną IL.

Czy są jakieś przykłady, w których oba języki generowały tak różne IL?

+4

brakuje 1 i 2 VB kod IL fragmenty –

+1

ja nie znam odpowiedzi, ale jeśli nikt nie odpowiada ... Zastanawiam się, czy to jest tak dużo różnica język jako różnica pomiędzy wprowadzaniem do Entity Framework dla VB vs C#? – user179700

+0

Mam podobne doświadczenia. Chociaż nie jest to pełny dowód, znalazłem, że użycie Linq/Lambda zamiast powyższej składni zapytań jest o wiele bliższe implementacji między C#/VB. to znaczy'Dim someEntities = ctx.SomeEntities.Where (funkcja (n) n.SomeId.Equals (5))' i 'var someEntities = ctx.SomeEntities.W miejscu (n => n.SomeId.Equals (5)) pojawia się uzyskać znacznie bliżej znaczniki IL. Nie jesteś pewien, czy to pomaga, czy jest w ogóle istotne? Mogę się tylko domyślać, dlaczego, ale są wyraźne i oczywiste różnice między tymi dwoma językami, gdy używamy składni zapytania, ale używając Lambdy możesz je łatwo/bezpośrednio przekonwertować? – Smudge202

Odpowiedz

5

Część różnic, które widzisz, może być spowodowana wywołaniem końca Wybierz x. Ponieważ nie jest wymagana w składni kwerendy VB, ale wyraźnie ją deklarujesz, VB uwzględnia ją w kompilacji. Mogłeś podano składnię VB następująco tak łatwo:

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

Ponieważ C# wymaga zasadniczo nie-kar- klauzuli SELECT w czasie kompilacji, kompilator optymalizuje go w wygenerowanym IL.

Podejrzewam, że w tym przykładzie zauważysz większe różnice między wygenerowanymi drzewami wyrażeń między VB i C#, jeśli użyłeś CustomerName = (=) "Foo", ponieważ C# i VB mają bardzo różne traktowanie równości łańcuchowej. Widziałem wielu dostawców LINQ (w tym LINQ do Bing, LINQ do Twittera, EF Sample Query Provider, NOrm), którzy nie udowodnili CustomerName = "Foo" w VB, ponieważ przetestowali tylko ich analizę drzewa ekspresji w C#.

Jeśli chodzi o twoje twierdzenie, że C# buforuje wyniki, nie widzę tego używając następującego kodu przeciw Northwind (używając LinqPada). Wciąż wywołuje bazę danych 3 razy.

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());   
} 
+0

Bardzo czyste wyjaśnienie thx! –

2

Zarówno kod wywoła bazę danych za każdym razem, gdy wywoływana jest metoda CallContext. Różnica tylko w drzewie wyrażeń generowanym dla wyrażenia LINQ, co w tym przypadku nie jest dużą różnicą.

Jak już powiedziałeś w swoim komentarzu, że teraz użyłeś metody łańcuchowej LINQ zamiast składni LINQ, dlatego teraz wygenerowane drzewo wyrażeń będzie takie samo.

+0

Myślę, że masz rację. Dziwne, ponieważ w książce "Dostęp do danych za pomocą Microsoft .NET Framework 4", rozdział 4, lekcja 3, mówią inaczej ... Ale kiedy uruchamiam profile, uzyskuję różne pytania. Nienawidzę książek MS: s –

+0

Istnieje optymalizacja, jeśli pobierasz przez ID przy użyciu metod takich jak First, Single, itp., Gdzie rekord jest buforowany przy pierwszym uruchomieniu i zwróceniu z pamięci podręcznej zamiast ponownego wysyłania zapytania do bazy danych. Twoje zapytania tego nie robią, więc w tym przypadku wymagane jest pełne pobranie. Jeśli jesteś zainteresowany, mogę polecić dobrą alternatywną książkę LINQ z Manning Press (www.LinqInAction.net) ;-) –