Est-ce une bonne situation pour utiliser une constante?


42

Donc, mon professeur donnait des informations sur un projet sur lequel je travaillais. Il a amarré quelques marques pour ce code:

if (comboVendor.SelectedIndex == 0) {
  createVendor cv = new createVendor();
  cv.ShowDialog();
  loadVendors();
}

C'est dans un combobox "index changé" gestionnaire. Il est utilisé lorsque l'utilisateur souhaite créer un nouveau fournisseur. Mon option supérieure (index 0, qui ne change jamais) ouvre la boîte de dialogue "Créer un nouveau fournisseur". Ainsi, le contenu de ma liste déroulante se présente comme suit:

Create New Vendor...
Existing Vendor
Existing Vendor 2
Existing Vendor 3

Son problème est avec le code de première ligne:

if (comboVendor.SelectedIndex == 0)

Il prétend que le 0 devrait être une constante et m'a en fait amputé les marques à cause de cela. Il affirme que je ne devrais pas du tout utiliser de littéral dans mon code.

Le problème est que je ne comprends pas pourquoi je voudrais faire de ce code une constante. Cet index ne changera jamais, et ce n’est pas quelque chose que vous auriez besoin de peaufiner. Cela semble être un gaspillage de mémoire de garder un seul 0 en mémoire, utilisé pour une situation très spécifique et qui ne change jamais.


32
Il est dogmatique. Les nombres magiques sont en général une bonne chose à éviter. Je pense que -1, 0 et 1 peuvent être considérés comme des exceptions à cette règle. De plus, une constante comme celle-ci ne prendrait pas plus de place que le 0 littéral.
Dave Mooney

23
@ DaveMooney: Êtes-vous sûr de ne pas avoir de réaction réflexe ici? Il est vrai que des choses comme l’ -1in str.indexOf(substr) != -1pour « strcontient substr» sont parfaitement justifiées. Mais ici, la signification du 0 n’est ni évidente (quelle est la relation avec la création d’un nouveau fournisseur?) Ni vraiment constante (et si le moyen de créer un nouveau fournisseur change?).

62
il faut apprendre les règles avant d'en arriver à les enfreindre
Ryathal

12
J'ai eu cette méthode a échoué sur moi de façon inattendue. La liste a été triée par ordre alphabétique. J'ai utilisé - - Créer nouveau - - afin que les tirets trient en premier et l'index codé en dur 0 selon la méthode CreateNew. Ensuite, quelqu'un a ajouté un élément commençant par une citation unique "Mon élément", trié avant les tirets. Mon codage dur a provoqué le blocage du programme lors du prochain chargement de la liste. J'ai dû modifier manuellement le fichier de données de la liste pour récupérer les données du client.
Hand-E-Food

14
Vous pouvez utiliser à la int.Zeroplace pour le rendre heureux :)
Paul Stovell

Réponses:


90

La bonne façon de faire cela en C # est de ne pas se fier à la commande des ComboItems .

public partial class MyForm : Form
{
    private readonly object VENDOR_NEW = new object();

    public MyForm()
    {
        InitializeComponents();
        comboVendor.Items.Insert(0, VENDOR_NEW);
    }

    private void comboVendor_Format(object sender, ListControlConvertEventArgs e)
    {
        e.Value = (e.ListItem == VENDOR_NEW ? "Create New Vendor" : e.ListItem);
    }

    private void comboVendor_SelectedIndexChanged(object sender, EventArgs e)
    {
        if(comboVendor.SelectedItem == VENDOR_NEW)
        {
            //Special logic for selecting "create new vendor"
        }
        else
        {
            //Usual logic
        }
    }
}

22
Eh bien, s'il s'agit de C #, vous ne devriez pas utiliser ALL_CAPS pour les constantes. Les constantes doivent être PascalCased - stackoverflow.com/questions/242534/…
Groky

4
@Groky: Pourquoi est-ce important? Qui se soucie de comment il nomme ses constantes? Ceci est correct à 100% s'il utilise ALL_CAPS de manière cohérente pour les constantes.
marco-fiset

4
@marcof: Eh bien, si les constantes font partie d'une interface publique, il devrait alors suivre les directives de nommage de MS. Sinon, il devrait au moins apprendre tôt les meilleures pratiques.
Groky

2
Ceci est toujours incorrect. Il déplace le problème d'avoir un littéral entier (zéro) en un littéral de chaîne (Créer un nouveau fournisseur). Au mieux, le problème est maintenant "que se passe-t-il si l'étiquette change" au lieu de "que se passe-t-il si l'index change"?
Freiheit

7
@ Freiheit: Incorrect. La constante de chaîne ici est uniquement pour l'affichage; cela n'affecte en rien la logique du programme. Contrairement à l'utilisation de valeurs / chaînes magiques pour stocker l'état du programme, modifier cette chaîne (ou ajouter / supprimer des éléments de la liste) ne peut jamais rien casser.
BlueRaja - Danny Pflughoeft

83

L'ordre dans la liste déroulante peut changer. Que faire si vous ajoutez une autre option comme "Créer un fournisseur spécial ..." avant votre "Créer un nouveau Vender ..."

L'avantage d'utiliser une constante est que s'il existe de nombreuses méthodes qui dépendent de l'ordre de la liste déroulante, il vous suffit de modifier la constante et non toutes les méthodes si cela change.

L'utilisation d'une constante est également plus lisible qu'un littéral.

if (comboVendor.SelectedIndex == NewVendorIndex)

La plupart des langages compilés substitueront la constante au moment de la compilation, il n'y aura donc aucune pénalité de performance.


5
J'utiliserais un code descriptif dans l'attribut value plutôt que de compter sur un index qui peut changer (le fait de déplacer l'option de création au bas de la liste rompra absolument la méthode de la constante). Ensuite, cherchez ce code.
CaffGeek

1
@Chad, je suis d'accord pour identifier les commandes par la commande dans une interface graphique est très fragile. Si le langage prend en charge l'ajout d'une valeur à l'élément d'interface graphique qui peut être recherchée, je l'utilise.
Michael Krussel

3
@ solution parce que c'est la convention.
Ikke

3
@ Ikke Ce n'est certainement pas la convention. stackoverflow.com/questions/242534/…
SolutionYogi

1
Si nous parlons de C #, NewVendorIndex EST la convention. Cela correspond au reste du style .NET.
MaR

36

Cette situation que vous décrivez est un jugement. Personnellement, je n’en utiliserais pas une si elle n’est utilisée qu’une fois et est déjà lisible.

La vraie réponse est cependant qu'il a choisi ceci pour vous donner une leçon.

N'oubliez pas qu'il est professeur, son travail consiste à vous enseigner le codage et les meilleures pratiques.

Je dirais qu'il fait un très bon travail en fait.

Bien sûr, il est peut-être un peu absolu, mais je suis certain que vous réfléchirez à nouveau avant d'utiliser des nombres magiques.

En outre, il s'est suffisamment impliqué pour que vous rejoigniez une communauté en ligne sur les programmeurs, simplement pour découvrir ce qui est considéré comme une pratique exemplaire dans cette situation.

Chapeau à votre professeur.


+1 pour indiquer que, dans ce cas, les moyens justifient le résultat final
oliver-clare

@ Thanos- " N'oubliez pas qu'il est professeur, son travail consiste à vous enseigner le codage et les meilleures pratiques. " Cela n'a jamais été le cas pour mon professeur. Je dirais que son travail consiste à vous enseigner ce que le ministère juge important une fois le cours créé.
Ramhound

13

[...] ma première option (index 0, qui ne change jamais) ouvre la boîte de dialogue "Créer un nouveau fournisseur" ".

Le fait que vous ayez dû expliquer cela prouve pourquoi vous devriez utiliser une constante. Si vous introduisiez une constante semblable à NEW_VENDOR_DIALOG, votre code serait plus explicite. En outre, les compilateurs optimisent les constantes pour ne pas altérer les performances.

Écrire des programmes pour les programmeurs, pas les compilateurs. Sauf si vous essayez spécifiquement de micro-optimiser, ce qui ne semble pas vous ressembler.


2
-1 pour même mentionner la performance. Même s'il n'est pas optimisé, un ordinateur peut effectuer des milliards d'opérations de ce type avant que l'utilisateur ne le remarque.
Boris Yankov

3
@Boris OP semblait préoccupé par le gaspillage de ressources, c'est pourquoi je l'ai mentionné. Je ne vois pas en quoi ma réponse serait moins correcte à cause de cela.
kba

12

Il prétend que le 0 devrait être une constante et m'a en fait amputé les marques à cause de cela.

Je serais d'accord L'utilisation de zéro ici est "magique". Imaginez que vous lisiez ce code pour la première fois. Vous ne savez pas pourquoi zéro est spécial et le littéral ne vous dit rien sur la raison pour laquelle zéro est spécial. Si au lieu de cela vous avez dit, le if(comboVendor.SelectedIndex == CreateNewVendorIndex)lecteur qui lit pour la première fois sa signification du code devient extrêmement clair.

Il affirme que je ne devrais pas du tout utiliser de littéral dans mon code.

C'est une position extrême. une position réaliste serait de dire que l'utilisation de littéraux est un drapeau rouge indiquant que le code peut ne pas être aussi clair qu'il pourrait l'être. Parfois, c'est approprié.

Le problème est que je ne comprends pas pourquoi je voudrais faire de ce code une constante. Cet indice ne changera jamais

Le fait que cela ne changera jamais est une excellente raison pour en faire une constante . C'est pourquoi les constantes sont appelées constantes; parce qu'ils ne changent jamais.

ce n'est pas quelque chose que vous auriez besoin de peaufiner.

Vraiment? Vous ne pouvez pas voir une situation dans laquelle quelqu'un pourrait vouloir changer l'ordre des choses dans une liste déroulante?

Le fait que vous puissiez voir une raison pour laquelle cela pourrait changer à l'avenir est une bonne raison de ne pas en faire une constante. Plutôt, il devrait s'agir d'un champ entier statique non constant à lecture seule. Une constante doit être une quantité garantie de rester la même pour tous les temps . Pi et le numéro atomique de l'or sont de bonnes constantes. Les numéros de version ne sont pas; ils changent chaque version. Le prix de l'or est évidemment une terrible constante; ça change toutes les secondes. Ne faites que des choses constantes qui ne changent jamais .

Cela semble être un gaspillage de mémoire de garder un seul 0 en mémoire, utilisé pour une situation très spécifique et qui ne change jamais.

Nous arrivons maintenant au coeur de la question.

C’est peut-être la ligne la plus importante de votre question car elle indique que vous avez une compréhension très erronée de (1) la mémoire et (2) de l’optimisation. Tu es à l'école pour apprendre, et ce serait le bon moment pour bien comprendre les principes fondamentaux. Pouvez-vous expliquer en détail pourquoi vous croyez que "garder un seul zéro en mémoire est une perte de mémoire"? Tout d'abord, pourquoi croyez-vous qu'il est pertinent d'optimiser l'utilisation de quatre octets de mémoire dans un processus comportant au moins deux milliards d'octets de stockage adressable par l'utilisateur? Deuxièmement, quelle ressource utilisez-vous ici, selon vous ? Qu'entendez-vous par "mémoire" consommée?

Je suis intéressé par les réponses à ces questions d’abord parce que c’est une occasion pour vous de comprendre comment votre compréhension de l’optimisation et de la gestion de la mémoire est incorrecte, et ensuite parce que je veux toujours savoir pourquoi les débutants croient des choses bizarres pour pouvoir concevoir. de meilleurs outils pour les amener à avoir des croyances correctes.


6

Il a raison. Tu as raison. Vous vous trompez.

Il a raison, conceptuellement, d'éviter les nombres magiques. Les constantes rendent le code plus lisible en ajoutant un contexte à la signification du nombre. À l'avenir, lorsque quelqu'un lira votre code, il saura pourquoi un numéro particulier a été utilisé. Et si vous devez modifier une valeur quelque part sur la ligne, il est préférable de la modifier dans un endroit plutôt que d'essayer de traquer partout où un nombre particulier est utilisé.

Cela étant dit, vous avez raison. Dans ce cas précis, je ne pense vraiment pas qu'une constante soit justifiée. Vous recherchez le premier élément de la liste, qui est toujours zéro. Il ne sera jamais 23. Ou -pi. Vous recherchez spécifiquement zéro. Je ne pense vraiment pas qu'il faille encombrer le code en le rendant constant.

Cependant, vous vous trompez en supposant qu'une constante est transmise comme une variable, "utiliser la mémoire". Une constante est là pour l'humain et le compilateur. Il indique au compilateur de mettre cette valeur à cet endroit lors de la compilation, où vous auriez autrement mis un nombre littéral. Et même si la mémoire était constante, pour les applications les plus exigeantes, la perte d'efficacité ne serait même pas mesurable. S'inquiéter de l'utilisation de la mémoire d'un seul entier tombe définitivement dans «l'optimisation prématurée».


1
J'aurais presque voté cette réponse, si ce n'était du troisième paragraphe, affirmant que l'utilisation du littéral 0 est suffisamment claire et qu'aucune constante n'est justifiée. Une solution bien meilleure aurait été de ne pas s’appuyer sur l’indexation de l’élément de la liste déroulante, mais plutôt sur la valeur de l’élément sélectionné. Les constantes magiques sont des constantes magiques même si elles sont égales à 0 ou 3.14 - nommez-les correctement car cela rend le code plus lisible.
Roland Tepp

Il serait peut-être préférable d’utiliser la valeur de la liste, mais ce n’était pas le propos de sa question. Sa question était de savoir si c'était un usage approprié pour une constante. Et si on compare à 0 dans le contexte de l'interface avec l'interface graphique (pour une raison quelconque - peut-être quelqu'un recherche-t-il le premier élément indépendamment de la valeur), je pense qu'il serait inutile d'utiliser une constante à cet endroit.
GrandmasterB

Je ne serais pas d’accord… Rien qu’en regardant le code, il n’est pas immédiatement évident de comprendre la signification de 0 - il se peut qu’il cherche toujours le premier élément de la liste et c’est tout, lirait beaucoup plus propre, si la comparaison était 'comboVendor.SelectedIndex == FirstIndex' à la place?
Roland Tepp

2

J'aurais remplacé le 0avec une constante pour clarifier le sens, tel que NewVendorIndex. Vous ne savez jamais si votre commande va changer.


1

C'est la préférence totale de votre professeur. En règle générale, vous n'utilisez une constante que si le littéral sera utilisé plusieurs fois, si vous souhaitez que le lecteur comprenne le but de la ligne ou si votre littéral sera éventuellement modifié à l'avenir, et vous ne souhaitez que changer. dans un endroit. Cependant, pour ce semestre, le professeur est patron, je le ferais désormais dans cette classe.

Bonne formation pour le monde de l'entreprise? Très probablement.


2
Je ne suis pas d'accord avec la partie "utilisez uniquement une constante si le littéral sera utilisé plusieurs fois". Avec 0 (et peut-être -1, 1), c'est souvent clair, dans la plupart des cas, il est bon de donner un nom à une chose beaucoup plus claire en lisant le code.
johannes

1

Pour être honnête, bien que je ne pense pas que votre code soit la meilleure pratique, sa suggestion est franchement un peu grotesque.

Une pratique plus courante pour une liste déroulante .NET consiste à attribuer une valeur vide à l'élément "Sélectionner ...", tandis que les éléments réels ont des valeurs significatives, puis:

if (string.IsNullOrEmpty(comboVendor.SelectedValue))

plutôt que

if (comboVendor.SelectedIndex == 0)

3
Dans votre exemple, null est simplement un autre littéral. Le cœur de cette leçon est que les littéraux doivent être évités.
Enlevé

@overslacked - Il n'y a pas de littéral nul dans mon exemple.
Carson63000

Même s'il existait un littéral nul, null est un littéral acceptable, je ne pense même pas qu'il existe un moyen de vérifier si une référence à un objet est null sans utiliser la vérification si son égal à null.
Ramhound

Carson63000 - Je parlais de votre utilisation de IsNullOrEmpty. Vous échangez une "valeur magique" pour une autre. @Ramhound - Null est un littéral acceptable dans certains cas, pas de doute, mais je ne crois pas que ce soit un bon exemple (utiliser null ou un blanc comme valeur magique).
Enlevé

-1 pour "un peu grotesque". Bien que vous ayez raison de dire que l'utilisation de la valeur serait plus conventionnelle et plus facile à comprendre, si vous utilisez l'
index

1

Il n'a pas tort de souligner l'importance d'utiliser des constantes et vous n'avez pas tort d'utiliser des littéraux. Sauf s'il a souligné que c'est le style de codage attendu, vous ne devriez pas perdre de points pour l'utilisation de littéraux car ils ne sont pas nocifs. J'ai vu des littéraux utilisés un peu partout dans le code commercial.

Son point est bon cependant. C'est peut-être sa manière de vous faire prendre conscience des avantages des constantes:

1-Ils protègent votre code dans une certaine mesure contre la falsification accidentelle

2-Comme @DeadMG le dit dans sa réponse, si la même valeur littérale est utilisée à de nombreux endroits, elle peut apparaître avec une valeur différente par erreur. Les constantes préservent donc la cohérence.

3-Constants préservent le type, vous n'avez donc pas à utiliser quelque chose comme 0F pour signifier zéro.

4-Pour faciliter la lecture, COBOL utilise ZERO comme mot réservé pour la valeur zéro (mais vous permet également d’utiliser le zéro littéral). Il est donc parfois utile de donner un nom à une valeur, par exemple: (Source: ms-Constants

class CalendarCalc
{
    const int months = 12;
    const int weeks = 52; //This is not the best way to initialize weeks see comment
    const int days = 365;

    const double daysPerWeek = (double) days / (double) weeks;
    const double daysPerMonth = (double) days / (double) months;
}

ou comme dans votre cas (comme indiqué dans la réponse de @Michael Krussel)


2
Cela ne va-t-il pas être une définition étrange des jours par semaine? Il y a 7 jours par semaine pas 7.4287143
Winston Ewert

@ WinstonEwert, merci pour votre commentaire. 365/52 = 7.01923076923077 = 7 + (1/52). Maintenant, si vous supprimez la partie décimale et calculez 7 * 52, vous obtenez 364 jours, ce qui n’est pas le nombre correct de jours dans une année. Avoir une fraction est plus précis que la perdre (puisque vous pouvez formater le résultat pour qu'il montre 7 si vous voulez seulement afficher le nombre). Quoi qu'il en soit, il ne s'agissait que d'un exemple fourni par MS sur les constantes, mais votre argument est intéressant.
NoChance

1
Bien sûr, c’est juste un exemple, mais je m'oppose à votre affirmation selon laquelle il est plus précis d’inclure la fraction. Une semaine est définie comme 7 jours. Étant donné votre définition, 26 semaines correspondent à 182,5 jours, ce qui n’est tout simplement pas exact. Vraiment, le problème est votre int weeks = 52, il n'y a pas 52 semaines par an. Il y a 52.142857142857146 semaines dans une année, et c'est le nombre que vous devriez garder les fractions. Bien sûr, la seule chose qui est en réalité constante dans tout cet ensemble de constantes est le nombre de mois.
Winston Ewert

0

Vous n'auriez besoin de le mettre dans une constante que si sa dérivation est complexe ou si elle est répétée souvent. Sinon, un littéral va bien. Mettre tout dans une constante est une perte de temps totale.


Seulement si vous envisagez deux fois souvent et vous n'avez jamais besoin de modifier le code. Il est très facile de créer des bogues en modifiant l’un des deux cas.
BillThor

0

En fait, comme mentionné, si sa position change? Ce que vous pourriez / devriez faire est d’utiliser un code plutôt que de vous fier à un index.

Ainsi, lorsque vous créez votre liste de sélection, il se termine par HTML, comme

<select>
    <option value='CREATE'>Create New Vendor...</option>
    <option value='1'>Existing Vendor</option>
    <option value='2'>Existing Vendor 2</option>
    <option value='3'>Existing Vendor 3</option>
</select>

Ensuite, au lieu de vérifier selectedIndex === 0, vérifiez que la valeur est CREATECODEconstante et aurait été utilisée pour ce test et lors de la création de la liste de sélection.


3
Probablement une bonne approche, mais vu qu'il utilise C #, le code HTML n'est pas l'exemple le plus optimiste.
Winston Ewert

0

Je m'en débarrasserais tout à fait. Il suffit de mettre un bouton créer un nouveau à côté de la liste de la liste déroulante. Double-cliquez sur un élément de la liste pour le modifier ou cliquez sur le bouton. La nouvelle fonctionnalité n’a pas été enterrée dans la liste déroulante. Ensuite, le nombre magique est complètement supprimé.

En général, tout nombre littéral dans le code doit être défini comme une constante afin de placer le contexte autour du nombre. Qu'est-ce que zéro signifie? Dans ce cas, 0 = NEW_VENDOR. Dans d'autres cas, cela peut vouloir dire quelque chose de différent, c'est donc une bonne idée de mettre en contexte et de maintenir la capacité de maintenance.


0

Comme d'autres l'ont dit, vous devez utiliser une méthode autre que le numéro d'index pour identifier l'élément de la liste déroulante correspondant à une action donnée. ou, vous pouvez trouver l'index avec une logique de programmation et le stocker dans une variable.

La raison pour laquelle je vous écris est pour répondre à votre commentaire sur "l'utilisation de la mémoire". En C #, comme dans la plupart des langages, les constantes sont "pliées" par le compilateur. Par exemple, compilez le programme suivant et examinez l’IL. Vous constaterez que tous ces chiffres ne parviennent même pas dans le livre intérieur, sans parler de la mémoire de l'ordinateur:

public class Program
{
    public static int Main()
    {
        const int a = 1000;
        const int b = a + a;
        const int c = b + 42;
        const int d = 7928345;
        return (a + b + c + d) / (-a - b - c - d);
    }
}

IL résultant:

.method public hidebysig static 
    int32 Main () cil managed 
{
    .maxstack 1
    .locals init (
        [0] int32 CS$1$0000
    )

    IL_0000: nop
    IL_0001: ldc.i4.m1  // the constant value -1 to be returned.
    IL_0002: stloc.0
    IL_0003: br.s IL_0005

    IL_0005: ldloc.0
    IL_0006: ret
}

Ainsi, que vous utilisiez une constante, un littéral ou un kilo-octet de code en utilisant l'arithmétique constante, la valeur est traitée littéralement dans l'IL.

Un point lié: le pliage constant s’applique aux littéraux de chaîne. Beaucoup pensent qu'un tel appel provoque une trop grande concaténation inutile de chaînes:

public class Program
{
    public static int Main()
    {
        const string a = "a";
        const string b = a + a;
        const string c = "C";
        const string d = "Dee";
        return (a + b + c + d).Length;
    }
}

Mais vérifie l'IL:

IL_0000: nop
IL_0001: ldstr "aaaCDee"
IL_0006: callvirt instance int32 [mscorlib]System.String::get_Length()
IL_000b: stloc.0
IL_000c: br.s IL_000e
IL_000e: ldloc.0
IL_000f: ret

Ligne de fond: les opérateurs sur les expressions constantes génèrent des expressions constantes et le compilateur effectue tous les calculs; cela n'affecte pas les performances d'exécution.

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.