Question Différences entre la manière dont C # et VB traitent les paramètres nommés?


Maintenant que C # prend en charge les paramètres nommés, je vérifiais si celui-ci était implémenté de la même manière que VB, et je constatais une légère différence. Prenons par exemple une fonction de bibliothèque comme celle-ci:

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

En C #, si vous l'appelez comme ceci:

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

Le compilateur produit les instructions IL suivantes:

.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 

Ce qui se traduit par le code C # suivant:

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

Dans VB, si vous l'appelez comme ceci:

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

Le compilateur produit les instructions IL suivantes:

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 

Ce qui se traduit par le code VB suivant:

Foo("a", "b");

La manière dont VB requiert beaucoup moins d’appels d’instruction, c’est donc un avantage par rapport à la manière dont C # l’implémente? Si vous n'utilisez pas les paramètres nommés, C # produit la même chose que VB.


MODIFIER: Maintenant que nous avons déterminé que les instructions supplémentaires disparaissent en mode de publication, existe-t-il une raison particulière pour qu'elles soient présentes en mode débogage? VB agit de la même manière dans les deux modes, et C # n’insère pas les instructions supplémentaires lorsqu’on appelle la méthode normalement sans paramètres nommés (y compris lorsque vous utilisez des paramètres facultatifs).


18
2018-02-25 04:23


origine


Réponses:


Y a-t-il une raison particulière pour qu'ils soient présents en mode débogage?

La différence est entre:

  • poussez une valeur temporaire sur la pile, utilisez-la, jetez-la et
  • stocker une valeur temporaire dans un emplacement de pile spécifique, en faire une copie sur la pile, l'utiliser, la supprimer, mais la copie d'origine de la valeur temporaire reste dans le logement de pile

L'effet visible de cette différence est que le ramasse-miettes ne peut pas être aussi agressif pour nettoyer la valeur. Dans le premier scénario, la valeur peut être collectée immédiatement après le retour de l'appel. Dans le second scénario, la valeur est uniquement collectée après le retour de la méthode en cours (ou si l'emplacement est réutilisé).

Rendre le ramasse-miettes moins agressif facilite souvent les scénarios de débogage.

La question implicite est:

Pourquoi la différence entre C # et VB?

Les compilateurs C # et VB ont été écrits par différentes personnes qui ont fait des choix différents sur le fonctionnement de leurs générateurs de code respectifs.

MISE À JOUR: Re: votre commentaire

Dans le compilateur C #, la génération IL non optimisée est essentiellement de la même structure que notre représentation interne de la fonctionnalité. Quand on voit un argument nommé:

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

où la méthode est, disons

void M(int x, int y) { }

nous représentons cela en interne comme si vous aviez écrit

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

Parce que nous voulons préserver l’évaluation de gauche à droite des effets secondaires de Q et R. Il s’agit d’une représentation interne raisonnable, et lorsque nous la codégeons en mode non optimisé, nous venons de coder le code directement à partir de la représentation interne avec guère de modifications.

Lorsque nous exécutons l'optimiseur, nous détectons toutes sortes de choses, comme le fait que personne n'utilise ces variables locales invisibles. Nous pouvons alors éliminer les habitants du codegen.

Je connais très peu la représentation interne de VB; Je n'ai pas travaillé sur le compilateur VB depuis 1995, et j'ai entendu dire que cela avait peut-être légèrement changé au cours des quinze dernières années. J'imagine qu'ils font quelque chose de similaire, mais je ne connais pas les détails de la façon dont ils représentent des paramètres nommés ou comment leur générateur de code les gère.

Ce que je veux dire, c'est que cette différence n'indique pas, à ma connaissance, une différence sémantique importante. Au contraire, cela montre que la construction non optimisée ne fait que cracher la représentation interne de haut niveau que nous avons générée et dont nous savons qu'elle a la sémantique souhaitée.


13
2018-02-25 16:20



Le compilateur produit le code C # suivant:

Non - le compilateur produit IL, que vous traduisez ensuite en C #. Toute ressemblance avec le code C # est purement accidentelle (et pas toutes les IL générées). pouvez être écrit comme C #). La présence de tous ces "nop" me dit que vous êtes en mode "debug". Je réessayerais en mode "release" - cela peut faire une grande différence pour ces choses.

Je l'ai tiré en mode libération, en utilisant:

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

Donnant:

.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 
}

Tellement identique


5
2018-02-25 05:03