2010-02-25 11 views
18

Ahora que C# admite parámetros con nombre, estaba verificando si se implementó de la misma manera que lo hizo VB, y encontré que hay una pequeña diferencia. Tomemos, por ejemplo, una función de biblioteca de esta manera:¿Diferencias entre cómo C# y VB manejan parámetros con nombre?

public static void Foo(string a, string b) 
{ 
    Console.WriteLine(string.Format("a: {0}, b: {1}", a, b)); 
} 

En C#, si se llama así:

Foo(a: "a", b: "b"); 

El compilador produce las siguientes instrucciones IL:

.locals init (
    [0] string CS$0$0000, 
    [1] string CS$0$0001) 
L_0000: nop 
L_0001: ldstr "a" 
L_0006: stloc.0 
L_0007: ldstr "b" 
L_000c: stloc.1 
L_000d: ldloc.0 
L_000e: ldloc.1 
L_000f: call void [TestLibrary]TestLibrary.Test::Foo(string, string) 
L_0014: nop 
L_0015: ret 

Que se traduce en el siguiente código C#:

string CS$0$0000 = "a"; 
string CS$0$0001 = "b"; 
Test.Foo(CS$0$0000, CS$0$0001); 

En VB, si se llama así:

Foo(a:="a", b:="b") 

El compilador produce las siguientes instrucciones IL:

L_0000: nop 
L_0001: ldstr "a" 
L_0006: ldstr "b" 
L_000b: call void [TestLibrary]TestLibrary.Test::Foo(string, string) 
L_0010: nop 
L_0011: nop 
L_0012: ret 

que se traduce en el siguiente código VB:

Foo("a", "b"); 

La forma en que lo hace VB requiere muchas menos llamadas de instrucciones, entonces ¿hay alguna ventaja en la forma en que C# lo implementa? Si no usa los parámetros nombrados, C# produce lo mismo que VB.


EDITAR: Ahora que hemos determinado que las instrucciones adicionales desaparecen en modo de lanzamiento, ¿hay una razón particular para que puedan estar presentes en el modo de depuración? VB actúa igual en ambos modos, y C# no inserta las instrucciones adicionales cuando se llama al método normalmente sin parámetros nombrados (incluso cuando se usan parámetros opcionales).

+0

Cambiar el orden de los parámetros, Foo (b: = "b", a: = "a") para ver la diferencia (sugerencia: orden de efectos secundarios) – adrianm

+0

¿Cuál es el código VB equivalente? –

+0

@adrianm una idea interesante, pero acabo de probarlo (por curiosidad) y eso genera la misma IL como 'Foo (a:" a ", b:" b ");' –

Respuesta

13

¿Hay alguna razón particular para que estén presentes en el modo de depuración?

es la diferencia entre:

  • empuje un valor temporal en la pila, lo utilizan, desprenderse de ella, y
  • almacenar un valor temporal en una ranura pila específica, hacer una copia de él en la pila, úselo, deséchelo, pero la copia original del valor temporal permanece en la pila

El efecto visible de esta diferencia es que el recolector de basura no puede ser tan agresivo con la limpieza el valor. En el primer escenario, el valor podría recopilarse inmediatamente una vez que la llamada regrese. En el segundo escenario, el valor solo se recopila después de que el método actual regrese (o el slot se reutilice).

Hacer que el recolector de basura sea menos agresivo a menudo ayuda en los escenarios de depuración.

La pregunta implícita es:

Por qué la diferencia entre C# y VB?

Los compiladores C# y VB fueron escritos por diferentes personas que tomaron decisiones diferentes sobre cómo funcionan sus respectivos generadores de código.

ACTUALIZACIÓN: Re: su comentario

En el compilador de C#, sin optimizar la generación de IL es esencialmente la misma estructura que la representación interna de la entidad. Cuando vemos un argumento con nombre:

M(y : Q(), x : R()); 

donde el método es, dicen

void M(int x, int y) { } 

que representan esto internamente como si hubieras escrito

int ytemp = Q(); 
int xtemp = R(); 
M(xtemp, ytemp); 

Porque queremos preservar la evaluación de izquierda a derecha de los efectos secundarios de Q y R. Esta es una representación interna razonable, y cuando la codificamos en modo no optimizado, simplemente codificamos el código directamente desde el representante interno sin apenas modificaciones.

Cuando ejecutamos el optimizador detectamos todo tipo de cosas, como el hecho de que nadie usa esas variables locales invisibles para nada. Entonces podemos eliminar a los locales del codegen.

Sé muy poco acerca de la representación interna de VB; No he trabajado en el compilador de VB desde 1995, y escuché que podría haber cambiado ligeramente en los últimos quince años. Me imagino que hacen algo similar, pero no conozco los detalles de cómo representan los parámetros nombrados ni cómo los maneja su generador de códigos.

Mi punto es que, a mi entender, esta diferencia no ilustra una importante diferencia semántica. Por el contrario, ilustra que la construcción no optimizada simplemente escupe cualquier representación interna de alto nivel que ocurrimos generar que sabemos que tiene la semántica deseada.

+1

@Eric ¡Esa es una buena explicación, gracias! En cuanto a la pregunta implícita, quise decir más en términos de "por qué tomaron esas decisiones", ya que asumí que había buenas razones para hacerlo. –

+0

+1 para enfatizar la evaluación de izquierda a derecha que ustedes parecen seguir religiosamente en C# para mantenerla constante (También refiriéndose a su ejemplo F() + G() * H() en otra respuesta) –

5

El compilador genera el siguiente código C#:

No - el compilador produce IL, que se está traduciendo a continuación, C#. Cualquier semejanza con el código C# es puramente accidental (y no todas las IL generadas pueden escribirse como C#). La presencia de todos esos "nop" me dice que estás en modo "depuración". Intentaría de nuevo en el modo "liberar", puede marcar una gran diferencia en estas cosas.

que lo puso en marcha en modo de lanzamiento, usando:

static void Main() 
{ 
    Foo(a: "a", b: "b"); 
} 

Dar:

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    .maxstack 8 
    L_0000: ldstr "a" 
    L_0005: ldstr "b" 
    L_000a: call void ConsoleApplication1.Program::Foo(string, string) 
    L_000f: ret 
} 

Así idénticos.

+0

Tienes razón, tenía el idioma al revés allí ... buena captura. Lo intenté de nuevo en modo de lanzamiento (que pensé que había intentado, pero parece que no). Las instrucciones C# IL coinciden con las de VB en ese modo. Eso todavía me deja pensando cuál es el razonamiento para producir las instrucciones adicionales en el modo de depuración, ya que VB parece hacerlo de la manera más corta en ambos modos, pero es bueno saber que no es un problema en el modo de lanzamiento. –

+0

@gshackles - Supongo que internamente utiliza la interpretación más simple disponible que * garantiza la corrección * durante la etapa de traducción (es decir, la versión en su pregunta), y luego se basa en etapas posteriores (opcionales) en el compilador para optimizarla. Tener una traducción de código muy literal hace que los depuradores sean más simples y confiables. –

Cuestiones relacionadas