web-dev-qa-db-fra.com

Avantages d'utiliser const au lieu de variables dans les méthodes

Chaque fois que j'ai des variables locales dans une méthode, ReSharper suggère de les convertir en constantes:

// instead of this:
var s = "some string";
var flags = BindingFlags.Public | BindingFlags.Instance;

// ReSharper suggest to use this:
const string s = "some string";
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;

Étant donné que ce sont des valeurs vraiment constantes (et non des variables), je comprends que ReSharper suggère de les changer en const.

Mais à part cela, existe-t-il un autre avantage lors de l'utilisation de const (par exemple, de meilleures performances) qui justifie l'utilisation de const BindingFlags au lieu du mot clé var maniable et lisible?

BTW: Je viens de trouver une question similaire ici: Resharper me suggère toujours de faire une chaîne const au lieu d'une chaîne , mais je pense qu'il s'agit davantage de champs d'une classe où ma question concerne la variable locale/consts.

70
M4N

Le compilateur générera une erreur si vous essayez d'affecter une valeur à une constante, vous empêchant ainsi éventuellement de la modifier accidentellement.

De plus, l'utilisation des constantes et des variables présente généralement un petit avantage en termes de performances. Cela a à voir avec la façon dont ils sont compilés sur le MSIL, par ce magazine MSDN Q&A :

Maintenant, partout où myInt est référencé dans le code, au lieu d'avoir à faire un "ldloc.0" pour obtenir la valeur de la variable, le MSIL charge juste la valeur constante qui est codée en dur dans le MSIL. En tant que tel, il y a généralement un petit avantage en termes de performances et de mémoire à utiliser des constantes. Cependant, pour les utiliser, vous devez avoir la valeur de la variable lors de la compilation et toutes les références à cette constante au moment de la compilation, même si elles se trouvent dans un assembly différent, auront cette substitution effectuée.

Les constantes sont certainement un outil utile si vous connaissez la valeur au moment de la compilation. Si ce n'est pas le cas, mais que vous voulez vous assurer que votre variable n'est définie qu'une seule fois, vous pouvez utiliser le mot clé readonly en C # (qui correspond à initonly dans MSIL) pour indiquer que la valeur de la variable ne peut être définie que dans le constructeur; après cela, c'est une erreur de le changer. Ceci est souvent utilisé lorsqu'un champ aide à déterminer l'identité d'une classe et est souvent défini égal à un paramètre constructeur.

82
landoncz

tl; dr pour les variables locales avec des valeurs littérales, const ne fait aucune différence.


Votre distinction des "méthodes internes" est très importante. Examinons-le, puis comparons-le avec les champs const.

Const variables locales

L'avantage seulement d'une variable locale const est que la valeur ne peut pas être réaffectée.

Cependant const est limité aux types primitifs (int, double, ...) et string, ce qui limite son applicabilité.

Digression: Il y a des propositions pour le compilateur C # pour permettre un concept plus général de locals "en lecture seule" ( ici ) qui étendrait cet avantage à d'autres scénarios. Cependant, ils ne seront probablement pas considérés comme const et auraient probablement un mot clé différent pour ces déclarations (c'est-à-dire let ou readonly var ou quelque chose comme ça).

Considérez ces deux méthodes:

private static string LocalVarString()
{
    var s = "hello";
    return s;
}

private static string LocalConstString()
{
    const string s = "hello";
    return s;
}

Construit en mode Release, nous voyons l'IL (abrégé) suivant:

.method private hidebysig static string LocalVarString() cil managed 
{
    ldstr        "hello"
    ret          
}

.method private hidebysig static string LocalConstString() cil managed 
{
    ldstr        "hello"
    ret          
}

Comme vous pouvez le voir, ils produisent tous les deux exactement le même IL. Que le s local soit const ou non n'a pas d'impact.

Il en va de même pour les types primitifs. Voici un exemple utilisant int:

private static int LocalVarInt()
{
    var i = 1234;
    return i;
}

private static int LocalConstInt()
{
    const int i = 1234;
    return i;
}

Et encore une fois, l'IL:

.method private hidebysig static int32 LocalVarInt() cil managed
{
    ldc.i4       1234
    ret          
}

.method private hidebysig static int32 LocalConstInt() cil managed
{
    ldc.i4       1234
    ret     
}

Encore une fois, nous ne voyons aucune différence. Il ne peut pas y avoir de différence de performances ou de mémoire ici. La seule différence est que le développeur ne peut pas réaffecter le symbole.

Champs const

Comparer un champ const avec un champ variable est différent. Un champ non const must doit être lu lors de l'exécution. Vous vous retrouvez donc avec IL comme ceci:

// Load a const field
ldc.i4       1234

// Load a non-const field
ldsfld       int32 MyProject.MyClass::_myInt

Il est clair de voir comment cela pourrait entraîner une différence de performances, en supposant que le JIT ne peut pas incorporer une valeur constante elle-même.

Une autre différence importante ici concerne les champs const publics qui sont partagés entre les assemblys. Si un assembly expose un champ const et qu'un autre l'utilise, la valeur réelle de ce champ est copiée au moment de la compilation. Cela signifie que si l'assembly contenant le champ const est mis à jour mais que l'assembly utilisant n'est pas recompilé, l'ancienne valeur (et éventuellement incorrecte) sera utilisée.

Expressions const

Considérez ces deux déclarations:

const int i = 1 + 2;
int i = 1 + 2;

Pour le formulaire const, l'addition doit être calculée au moment de la compilation, ce qui signifie que le nombre 3 est conservé dans l'IL.

Pour la forme nonconst, le compilateur est libre d'émettre l'opération d'addition dans l'IL, bien que le JIT appliquerait presque certainement une optimisation de pliage constante de base de sorte que le code machine généré serait identique.

Le compilateur C # 7.3 émet le ldc.i4.3 opcode pour les deux expressions ci-dessus.

21
Drew Noakes

Selon ma compréhension, les valeurs Const n'existent pas au moment de l'exécution - c'est-à-dire sous la forme d'une variable stockée dans un emplacement mémoire - elles sont incorporées dans le code MSIL au moment de la compilation. Et aurait donc un impact sur les performances. Plus de temps d'exécution ne serait pas nécessaire pour effectuer des tâches de gestion (contrôles de conversion/collecte des ordures, etc.) sur eux, là où les variables nécessitent ces vérifications.

14
YetAnotherUser

const est une constante de temps de compilation - cela signifie que tout votre code qui utilise la variable const est compilé pour contenir l'expression constante que la variable const contient - l'IL émis contiendra cette valeur constante elle-même.

Cela signifie que l'encombrement de la mémoire est plus petit pour votre méthode, car la constante ne nécessite pas d'allocation de mémoire au moment de l'exécution.

4
BrokenGlass

Outre la petite amélioration des performances, lorsque vous déclarez une constante, vous appliquez explicitement deux règles à vous-même et aux autres développeurs qui utiliseront votre code

  1. Je dois l'initialiser avec une valeur en ce moment, je ne peux pas le faire ailleurs.
  2. Je ne peux changer sa valeur nulle part.

Dans le code, tout est question de lisibilité et de communication.

3
Shrage Smilowitz

Une valeur const est également "partagée" entre toutes les instances d'un objet. Cela pourrait également entraîner une utilisation moindre de la mémoire.

Par exemple:

public class NonStatic
{
    int one = 1;
    int two = 2;
    int three = 3;
    int four = 4;
    int five = 5;
    int six = 6;
    int seven = 7;
    int eight = 8;
    int nine = 9;
    int ten = 10;        
}

public class Static
{
    static int one = 1;
    static int two = 2;
    static int three = 3;
    static int four = 4;
    static int five = 5;
    static int six = 6;
    static int seven = 7;
    static int eight = 8;
    static int nine = 9;
    static int ten = 10;
}

La consommation de mémoire est délicate dans .Net et je ne prétendrai pas en comprendre les moindres détails, mais si vous instanciez une liste avec un million de `` statiques '', elle utilisera probablement beaucoup moins de mémoire que si vous ne le faites pas.

    static void Main(string[] args)
    {
        var maxSize = 1000000;
        var items = new List<NonStatic>();
        //var items = new List<Static>();

        for (var i=0;i<maxSize;i++)
        {
            items.Add(new NonStatic());
            //items.Add(new Static());
        }

        Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().WorkingSet64);
        Console.Read();
    }

Lorsque vous utilisez "NonStatic", le jeu de travail est de 69 398 528, contre seulement 32 423 936 lorsque vous utilisez de l'électricité statique.

2
Rob P.

Le mot clé const indique au compilateur qu'il peut être entièrement évalué au moment de la compilation. Il y a un avantage en termes de performances et de mémoire, mais il est petit.

1
Chris Trombley

Les constantes en C # fournissent un emplacement nommé en mémoire pour stocker une valeur de données. Cela signifie que la valeur de la variable sera connue au moment de la compilation et sera stockée en un seul endroit.

Lorsque vous le déclarez, il est en quelque sorte "codé en dur" dans le langage intermédiaire Microsoft (MSIL).

Bien qu'un peu, cela peut améliorer les performances de votre code. Si je déclare une variable et que je peux en faire une constante, je le fais toujours. Non seulement parce qu'il peut améliorer les performances, mais aussi parce que c'est l'idée des constantes. Sinon, pourquoi existent-ils?

Reflector peut être très utile dans des situations comme celle-ci. Essayez de déclarer une variable, puis faites-en une constante, et voyez quel code est généré dans IL . Ensuite, tout ce que vous avez à faire est de voir la différence dans les instructions et de voir ce que ces instructions signifient.

1
Oscar Mederos