Сгенерированные различия IL для VB.NET и C # - PullRequest
6 голосов
/ 26 августа 2011

Сегодня я играл с Entity Framework и прочитал, что сгенерированный IL для C # отличается от VB.NET для следующего кода:

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

Они производят следующие 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

Как кажетсяверсия этого кода для VB.NET будет связываться с базой данных каждый раз, когда код выполняется, в то время как версия C # будет извлекать объекты из кэша при многократном выполнении кода.

Почему они заставляют оба языка вести себятаким другим образом?Я ошибочно полагал, что оба языка просто различаются по синтаксису и имеют почти одинаковый сгенерированный IL.

Есть ли еще примеры, когда оба языка генерировали такие разные IL?

Ответы [ 2 ]

5 голосов
/ 26 августа 2011

Часть различий, которые вы видите, может быть связана с вызовом конца. Выберите x. Поскольку это не требуется в синтаксисе запроса VB, но вы явно его объявляете, VB включает его в компиляцию. Вы могли бы так же легко сформулировать синтаксис VB следующим образом:

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

Поскольку C # требует по существу неиспользуемого предложения Select во время компиляции, компилятор оптимизирует его в сгенерированном IL.

Я подозреваю, что в этом примере вы увидите большие различия между сгенерированными деревьями выражений между VB и C #, если вы используете CustomerName = (=) "Foo", потому что C # и VB имеют очень разную обработку равенства строк. Я видел довольно много провайдеров LINQ (в том числе LINQ to Bing, LINQ to Twitter, EF Sample Quider Provider, NOrm), которые не могут преобразовать CustomerName = "Foo" в VB, потому что они тестировали только синтаксический анализ дерева выражений в C #.

Что касается вашего заявления о том, что C # кеширует результаты, я не вижу, чтобы при использовании следующего кода против Northwind (с использованием LinqPad). Он все еще вызывает базу данных 3 раза.

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());          
} 
2 голосов
/ 26 августа 2011

Оба кода будут вызывать базу данных каждый раз, когда вызывается метод CallContext. Разница только в дереве выражений, сгенерированном для выражения LINQ, которое в этом случае не сильно отличается.

Как вы сказали в своем комментарии, теперь вы использовали цепочку методов LINQ, а не синтаксис LINQ, поэтому теперь сгенерированное дерево выражений будет таким же.

...