L'utilisation de «var» affectera-t-elle les performances?


230

Plus tôt, j'ai posé une question sur la raisonvar pour laquelle je vois autant d'exemples utiliser le mot - clé et j'ai obtenu la réponse selon laquelle s'il n'est nécessaire que pour les types anonymes, il est néanmoins utilisé pour rendre l'écriture du code plus rapide / plus facile et simplement parce que.

En suivant ce lien ("C # 3.0 - Var is not objec"), j'ai vu que cela était varcompilé jusqu'au type correct dans l'IL (vous le verrez à mi-chemin de l'article).

Ma question est de savoir combien, le cas échéant, le code IL utilise le varmot - clé, et serait-il même proche d'avoir un niveau mesurable sur les performances du code s'il était utilisé partout?


1
La question a répondu il y a longtemps, je voulais juste ajouter une chose contre var - bien qu'elle soit résolue au moment de la compilation, elle n'est pas repérée correctement par "Find All References" de Visual Studio et "Find Usages" de Resharper si vous voulez trouver toutes les utilisations du type - et ça ne va pas être corrigé car ce serait trop lent.
KolA

Les variables @KolA déclarées fonctionnent vartrès certainement avec "Rechercher toutes les références" dans Visual Studio 2019, donc si jamais il était cassé, il a été corrigé. Mais je peux confirmer que cela fonctionne aussi loin que Visual Studio 2012, donc je ne sais pas pourquoi vous avez affirmé que cela ne fonctionnait pas.
Herohtar

@Herohtar essayez de suivre le code "class X {} X GetX () {return new X ();} void UseX () {var x = GetX ();}" et Find All References to X, the "var x = GetX ( ) "le bit n'est pas mis en évidence - dans la dernière version VS2019, c'est ce que je voulais dire. Il est cependant mis en évidence si vous utilisez "X x = GetX ()" au lieu de var
KolA

1
@KolA Ah, je vois ce que vous voulez dire - varne sera pas considéré comme une référence Xlorsque vous utilisez "Rechercher toutes les références" sur X. Fait intéressant, si vous utilisez « Trouver toutes les références » sur vardans cette déclaration, il va vous montrer des références à X(bien qu'il ne sera toujours pas la liste de la vardéclaration). De plus, lorsque le curseur est activé var, il mettra en surbrillance toutes les instances de Xdans le même document (et vice versa).
Herohtar

Réponses:


316

Il n'y a pas de code IL supplémentaire pour le varmot clé: l'IL résultant doit être identique pour les types non anonymes. Si le compilateur ne peut pas créer cet IL car il ne peut pas déterminer le type que vous aviez l'intention d'utiliser, vous obtiendrez une erreur de compilateur.

La seule astuce consiste à vardéduire un type exact où vous pourriez avoir choisi une interface ou un type parent si vous deviez définir le type manuellement.


Mise à jour 8 ans plus tard

Je dois le mettre à jour car ma compréhension a changé. Je crois maintenant qu'il peut être possible vard'affecter les performances dans le cas où une méthode renvoie une interface, mais vous auriez utilisé un type exact. Par exemple, si vous avez cette méthode:

IList<int> Foo()
{
    return Enumerable.Range(0,10).ToList();
}

Considérez ces trois lignes de code pour appeler la méthode:

List<int> bar1 = Foo();
IList<int> bar = Foo();
var bar3 = Foo();

Les trois compilent et s'exécutent comme prévu. Cependant, les deux premières lignes ne sont pas exactement les mêmes et la troisième ligne correspondra à la seconde plutôt qu'à la première. Parce que la signature de Foo()est de retourner un IList<int>, c'est ainsi que le compilateur va construire la bar3variable.

Du point de vue des performances, vous ne le remarquerez généralement pas. Cependant, il existe des situations où les performances de la troisième ligne peuvent ne pas être aussi rapides que celles de la première . Lorsque vous continuez à utiliser la bar3variable, le compilateur peut ne pas être en mesure de répartir les appels de méthode de la même manière.

Notez qu'il est possible (probablement même) que la gigue puisse effacer cette différence, mais ce n'est pas garanti. En règle générale, vous devez toujours considérer varcomme un non-facteur en termes de performances. Ce n'est certainement pas du tout comme utiliser une dynamicvariable. Mais dire que cela ne fait aucune différence peut être exagéré.


23
Non seulement l'IL doit être identique, mais il est identique. var i = 42; compile exactement le même code que int i = 42;
Brian Rasmussen

15
@BrianRasmussen: Je sais que votre message est ancien est ancien, mais je suppose var i = 42;(le type infers est int) n'est pas identique à long i = 42;. Ainsi, dans certains cas, vous pouvez émettre des hypothèses incorrectes sur l'inférence de type. Cela pourrait entraîner des erreurs d'exécution de cas insaisissables / de bord si la valeur ne correspond pas. Pour cette raison, il peut être judicieux d'être explicite lorsque la valeur n'a pas de type explicite. Ainsi, par exemple, var x = new List<List<Dictionary<int, string>()>()>()serait acceptable, mais var x = 42est quelque peu ambigu et devrait être écrit ainsi int x = 42. Mais à chacun ses goûts ...
Nelson Rothermel

50
@NelsonRothermel: var x = 42; n'est pas ambigu. Les littéraux entiers sont du type int. Si vous voulez un long littéral, vous écrivez var x = 42L;.
Brian Rasmussen

6
Uhm que signifie IL en C #? Je n'en ai jamais vraiment entendu parler.
puretppc

15
Dans votre exemple des 3 lignes de code qui se comportent différemment, la première ligne ne se compile pas . Les deuxième et troisième lignes, qui compilent toutes les deux , font exactement la même chose. Si Foorenvoyé un List, plutôt qu'un an IList, alors les trois lignes se compileraient mais la troisième ligne se comporterait comme la première ligne , pas la seconde.
Servy

72

Comme le dit Joel, le compilateur détermine au moment de la compilation quel type var devrait être, en fait, c'est juste une astuce que le compilateur effectue pour enregistrer les frappes, donc par exemple

var s = "hi";

est remplacé par

string s = "hi";

par le compilateur avant la génération de tout IL. L'IL généré sera exactement le même que si vous aviez tapé une chaîne.


26

Comme personne n'a encore mentionné de réflecteur ...

Si vous compilez le code C # suivant:

static void Main(string[] args)
{
    var x = "hello";
    string y = "hello again!";
    Console.WriteLine(x);
    Console.WriteLine(y);
}

Utilisez ensuite un réflecteur dessus, vous obtenez:

// Methods
private static void Main(string[] args)
{
    string x = "hello";
    string y = "hello again!";
    Console.WriteLine(x);
    Console.WriteLine(y);
}

Donc, la réponse est clairement sans performances d'exécution!


17

Pour la méthode suivante:

   private static void StringVsVarILOutput()
    {
        var string1 = new String(new char[9]);

        string string2 = new String(new char[9]);
    }

La sortie IL est la suivante:

        {
          .method private hidebysig static void  StringVsVarILOutput() cil managed
          // Code size       28 (0x1c)
          .maxstack  2
          .locals init ([0] string string1,
                   [1] string string2)
          IL_0000:  nop
          IL_0001:  ldc.i4.s   9
          IL_0003:  newarr     [mscorlib]System.Char
          IL_0008:  newobj     instance void [mscorlib]System.String::.ctor(char[])
          IL_000d:  stloc.0
          IL_000e:  ldc.i4.s   9
          IL_0010:  newarr     [mscorlib]System.Char
          IL_0015:  newobj     instance void [mscorlib]System.String::.ctor(char[])
          IL_001a:  stloc.1
          IL_001b:  ret
        } // end of method Program::StringVsVarILOutput

14

Le compilateur C # déduit le vrai type de la varvariable au moment de la compilation. Il n'y a aucune différence dans l'IL généré.


14

Donc, pour être clair, c'est un style de codage paresseux. Je préfère les types natifs, étant donné le choix; Je prendrai ce petit peu de «bruit» pour m'assurer d'écrire et de lire exactement ce que je pense être au moment du code / débogage. * haussement d'épaules *


1
C'est juste votre point de vue subjectif et non une réponse à la question sur les performances. La bonne réponse est qu'elle n'a aucun impact sur les performances. J'ai voté pour la fin
Anders

Cela ne répond pas à la question de savoir si varaffecte les performances du tout; vous exprimez simplement votre opinion quant à savoir si les gens devraient l'utiliser.
Herohtar

L'inférence de type de valeur plus tard, par exemple, le passage de int 5 à float 5.25, peut absolument causer des problèmes de performances. * haussement d'épaules *
ChrisH

Non, cela ne causera aucun problème de performances; vous obtiendrez des erreurs de construction à tous les endroits qui attendaient une variable de type intcar elle ne peut pas convertir automatiquement le float, mais c'est exactement la même chose qui se produirait si vous l'utilisiez explicitement intet que vous la modifiiez ensuite float. Dans tous les cas, votre réponse ne répond toujours pas à la question "Est-ce que l'utilisation varaffecte les performances?" (en particulier en termes d'IL généré)
Herohtar

8

Je ne pense pas que vous ayez bien compris ce que vous avez lu. S'il est compilé dans le bon type, il n'y a pas de différence. Quand je fais ça:

var i = 42;

Le compilateur sait que c'est un int et génère du code comme si j'avais écrit

int i = 42;

Comme le dit le message auquel vous avez lié, il est compilé sur le même type. Ce n'est pas une vérification d'exécution ou toute autre chose nécessitant du code supplémentaire. Le compilateur détermine simplement quel type doit être, et l'utilise.


Oui, mais si plus tard vous i = i - someVar et someVar = 3.3. je suis un Int, maintenant. Il vaut mieux être explicite non seulement pour donner au compilateur une longueur d'avance sur la recherche de failles, mais aussi pour minimiser les erreurs d'exécution ou les conversions de type qui ralentissent le processus. * haussement d'épaules * Il rend également le code meilleur pour l'auto-description. Je fais ça depuis très longtemps. Je prendrai du code "bruyant" avec des types explicites à chaque fois, étant donné le choix.
ChrisH

5

Il n'y a aucun coût de performance d'exécution à utiliser var. Cependant, je soupçonne qu'il y a un coût de performance de compilation car le compilateur doit déduire le type, bien que cela soit très probablement négligeable.


10
le RHS doit avoir son type calculé de toute façon - le compilateur intercepterait les types incompatibles et générerait une erreur, donc pas vraiment un coût là-bas, je pense.
Jimmy

3

Si le compilateur peut faire l'inférence de type automatique, il n'y aura aucun problème de performances. Les deux généreront le même code

var    x = new ClassA();
ClassA x = new ClassA();

cependant, si vous construisez le type dynamiquement (LINQ ...), varc'est votre seule question et il y a un autre mécanisme à comparer pour dire quelle est la pénalité.


3

J'utilise toujours le mot var dans les articles Web ou les écrits de guides.

La largeur de l'éditeur de texte de l'article en ligne est petite.

Si j'écris ceci:

SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

Vous verrez que le texte pré-code rendu ci-dessus est trop long et sort de la boîte, il est masqué. Le lecteur doit faire défiler vers la droite pour voir la syntaxe complète.

C'est pourquoi j'utilise toujours le mot-clé var dans les articles Web.

var coolClass = new SomeCoolNameSpace.SomeCoolClassName.SomeCoolSubClassName();

Le pré-code rendu entier tient juste dans l'écran.

En pratique, pour déclarer un objet, j'utilise rarement var, je compte sur intellisense pour déclarer un objet plus rapidement.

Exemple:

SomeCoolNamespace.SomeCoolObject coolObject = new SomeCoolNamespace.SomeCoolObject();

Mais, pour retourner un objet à partir d'une méthode, j'utilise var pour écrire du code plus rapidement.

Exemple:

var coolObject = GetCoolObject(param1, param2);

Si vous écrivez pour les élèves, alors mangez votre propre nourriture pour chien et écrivez-la toujours de la même manière «correcte», de manière cohérente. Les élèves prennent souvent les choses à 100% mot pour mot et à cœur, et commenceront à utiliser toutes les habitudes bâclées qu'ils auront en cours de route. 0,02 $
ChrisH

1

"var" est l'une de ces choses que les gens aiment ou détestent (comme les régions). Cependant, contrairement aux régions, var est absolument nécessaire lors de la création de classes anonymes.

Pour moi, var est logique lorsque vous actualisez directement un objet comme:

var dict = new Dictionary<string, string>();

Cela étant dit, vous pouvez facilement faire simplement:

Dictionary<string, string> dict = new et intellisense rempliront le reste pour vous ici.

Si vous ne souhaitez travailler qu'avec une interface spécifique, vous ne pouvez pas utiliser var sauf si la méthode que vous appelez renvoie directement l'interface.

Resharper semble être du côté de l'utilisation de "var" partout, ce qui peut pousser plus de gens à le faire de cette façon. Mais je conviens en quelque sorte qu'il est plus difficile à lire si vous appelez une méthode et ce n'est pas évident ce qui est retourné par le nom.

var lui-même ne ralentit pas les choses, mais il y a une mise en garde à laquelle peu de gens pensent. Si vous le faites, var result = SomeMethod();le code après cela attend une sorte de résultat où vous appelleriez diverses méthodes ou propriétés ou autre chose. Si SomeMethod()changé sa définition en un autre type mais qu'il respectait toujours le contrat que l'autre code attendait, vous venez de créer un bug vraiment désagréable (si pas de tests unitaires / d'intégration, bien sûr).


0

Cela dépend de la situation, si vous essayez d'utiliser ce code ci-dessous.

L'expression est convertie en "OBJET" et diminue tellement les performances, mais c'est un problème isolé.

CODE:

public class Fruta
{
    dynamic _instance;

    public Fruta(dynamic obj)
    {
        _instance = obj;
    }

    public dynamic GetInstance()
    {
        return _instance;
    }
}

public class Manga
{
    public int MyProperty { get; set; }
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
    public int MyProperty3 { get; set; }
}

public class Pera
{
    public int MyProperty { get; set; }
    public int MyProperty1 { get; set; }
    public int MyProperty2 { get; set; }
}

public class Executa
{
    public string Exec(int count, int value)
    {
        int x = 0;
        Random random = new Random();
        Stopwatch time = new Stopwatch();
        time.Start();

        while (x < count)
        {
            if (value == 0)
            {
                var obj = new Pera();
            }
            else if (value == 1)
            {
                Pera obj = new Pera();
            }
            else if (value == 2)
            {
                var obj = new Banana();
            }
            else if (value == 3)
            {
                var obj = (0 == random.Next(0, 1) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance());
            }
            else
            {
                Banana obj = new Banana();
            }

            x++;
        }

        time.Stop();
        return time.Elapsed.ToString();
    }

    public void ExecManga()
    {
        var obj = new Fruta(new Manga()).GetInstance();
        Manga obj2 = obj;
    }

    public void ExecPera()
    {
        var obj = new Fruta(new Pera()).GetInstance();
        Pera obj2 = obj;
    }
}

Résultats ci-dessus avec ILSPY.

public string Exec(int count, int value)
{
    int x = 0;
    Random random = new Random();
    Stopwatch time = new Stopwatch();
    time.Start();

    for (; x < count; x++)
    {
        switch (value)
        {
            case 0:
                {
                    Pera obj5 = new Pera();
                    break;
                }
            case 1:
                {
                    Pera obj4 = new Pera();
                    break;
                }
            case 2:
                {
                    Banana obj3 = default(Banana);
                    break;
                }
            case 3:
                {
                    object obj2 = (random.Next(0, 1) == 0) ? new Fruta(new Manga()).GetInstance() : new Fruta(new Pera()).GetInstance();
                    break;
                }
            default:
                {
                    Banana obj = default(Banana);
                    break;
                }
        }
    }
time.Stop();
return time.Elapsed.ToString();
}

Si vous souhaitez exécuter ce code, utilisez le code ci-dessous et obtenez la différence de temps.

        static void Main(string[] args)
    {
        Executa exec = new Executa();            
        int x = 0;
        int times = 4;
        int count = 100000000;
        int[] intanceType = new int[4] { 0, 1, 2, 3 };

        while(x < times)
        {                
            Parallel.For(0, intanceType.Length, (i) => {
                Console.WriteLine($"Tentativa:{x} Tipo de Instancia: {intanceType[i]} Tempo Execução: {exec.Exec(count, intanceType[i])}");
            });
            x++;
        }

        Console.ReadLine();
    }

Cordialement

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.