2011-08-26 13 views
6

Hoy estaba jugando con Entity Framework y he leído que el IL generado para C# fue diferente de VB.NET para el siguiente código:diferencias IL generado para VB.NET y 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()); 
    } 

producen la IL siguiente:

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 

Como parece la versión VB.NET de este código pondrá en contacto con la base de datos cada vez que el código se ejecuta mientras que la versión C# recuperará las entidades de la caché cuando el el código se ejecuta varias veces.

¿Por qué harían que ambos idiomas se comportaran de una manera tan diferente? Era mi concepto erróneo de que ambos lenguajes solo diferían en sintaxis y tenían casi exactamente el mismo IL generado.

¿Hay algún otro ejemplo en el que ambos lenguajes hayan generado una IL tan diferente?

+4

falta 1 y 2 VB código IL fragmentos –

+1

No sé la respuesta, pero si nadie responde ... Me pregunto si es tanto una diferencia de idioma como una diferencia entre la aplicación de Entity Framework para VB vs C#? – user179700

+0

He tenido experiencias similares. Aunque no a prueba total, he encontrado que usar Linq/Lambda en lugar de la sintaxis de consulta anterior es mucho más estrecha en la implementación entre C#/VB. es decir'Dim someEntities = ctx.SomeEntities.Where (Función (n) n.SomeId.Equals (5))' y 'var someEntities = ctx.SomeEntities.Where (n => n.SomeId.Equals (5))' parece obtener un markup IL mucho más cercano. ¿No estoy seguro de si eso ayuda o es relevante? Solo puedo adivinar por qué, pero hay diferencias claras y obvias entre los dos idiomas al usar la sintaxis de la consulta, pero cuando se usa Lambda, ¿puede convertirlos fácilmente/directamente? – Smudge202

Respuesta

5

Parte de las diferencias que está viendo se pueden deber a llamar al final Seleccionar x. Como no se requiere en la sintaxis de la consulta de VB pero usted lo declara explícitamente, VB lo incluye en la compilación. Que podría haber indicado la sintaxis VB de la siguiente manera tan fácilmente:

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

Puesto que C# requiere la cláusula select esencialmente no-artículo de opinión en tiempo de compilación, el compilador optimiza a cabo en el IL generado.

Sospecho que en este ejemplo, verías mayores diferencias entre los árboles de expresiones generados entre VB y C# si usaras CustomerName = (=) "Foo" porque C# y VB tienen un manejo muy diferente de la igualdad de cadenas. He visto bastantes proveedores de LINQ (incluyendo LINQ a Bing, LINQ a Twitter, EF Sample Query Provider, NOrm) que no evaden CustomerName = "Foo" en VB porque solo probaron el análisis del árbol de expresiones en C#.

En cuanto a su afirmación de que C# almacena en caché los resultados, no estoy viendo eso usando el siguiente código contra Northwind (usando LinqPad). Todavía está llamando a la base de datos 3 veces.

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

¡Explicación muy clara! –

2

Ambos códigos llamarán a la base de datos cada vez que se llame al método CallContext. La diferencia solo en el árbol de expresiones generado para la expresión LINQ, que en este caso no es muy diferente.

Como dijo en su comentario que ahora ha utilizado el encadenamiento de métodos LINQ en lugar de la sintaxis LINQ, es por eso que ahora el árbol de expresiones generado será el mismo.

+0

Creo que tienes razón. Extraño porque en el libro "Acceso a datos con Microsoft .NET Framework 4" capítulo 4 lección 3 dicen lo contrario ... Pero cuando ejecuto los perfiles obtengo consultas mutiples. Odio los libros MS: s –

+0

Hay una optimización si obtiene por ID usando métodos como Primero, Único, etc. donde el registro se almacena en caché la primera vez que se accede y se devuelve desde el caché en lugar de consultar la base de datos nuevamente. Sus consultas no están haciendo eso, por lo que se requiere una búsqueda completa en este caso. Si está interesado, puedo recomendarle un buen libro LINQ alternativo de Manning Press (www.LinqInAction.net) ;-) –

Cuestiones relacionadas