È una situazione corretta usare una costante?


42

Quindi il mio professore stava dando alcuni feedback su un progetto a cui stavo lavorando. Ha ancorato alcuni segni per questo codice:

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

Questo è in un gestore "indice modificato" combobox. Viene utilizzato quando l'utente desidera creare un nuovo fornitore, la mia opzione principale (indice 0, che non cambia mai) apre la finestra di dialogo "Crea un nuovo fornitore". Quindi il contenuto della mia casella combinata finisce così:

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

Il suo problema è con il codice della prima riga:

if (comboVendor.SelectedIndex == 0)

Afferma che lo 0 dovrebbe essere una costante e in realtà mi ha ancorato dei segni a causa di ciò. Afferma che non dovrei usare letterali nel mio codice.

Il fatto è che non capisco perché vorrei rendere quel codice in quella situazione una costante. Quell'indice non cambierà mai, né è qualcosa che dovresti modificare. Sembra uno spreco di memoria mantenere un singolo 0 in memoria che viene utilizzato per una situazione molto specifica e non cambia mai.


32
È dogmatico. I numeri magici sono generalmente una buona cosa da evitare. Penso che -1, 0 e 1 possano essere considerati eccezioni a tale regola. Inoltre, una costante come questa non occuperebbe più spazio del letterale 0.
Dave Mooney,

23
@DaveMooney: sei sicuro di non avere una reazione istintiva qui? E 'vero che le cose come la -1a str.indexOf(substr) != -1per " strcontiene substr" è prefectly giustificati. Ma qui, il significato di 0 non è né ovvio (quale è la relazione con la creazione di un nuovo fornitore?) Né veramente costante (cosa succede se cambia il modo di creare un nuovo fornitore?).

62
devi imparare le regole prima di arrivare a infrangere le regole
Ryathal,

12
Ho avuto questo metodo fallire su di me inaspettatamente. L'elenco è stato ordinato in ordine alfabetico. Ho usato - - Crea nuovo - - in modo che i trattini ordinassero l'indice 0 per primo e hard coded nel metodo CreateNew. Quindi qualcuno ha aggiunto un articolo che inizia con una singola citazione "Il mio articolo" che ordina prima dei trattini. La mia codifica rigida ha causato l'arresto anomalo del programma al successivo caricamento dell'elenco. Ho dovuto modificare manualmente il file di dati dell'elenco per recuperare i dati del cliente.
Hand-E-Food

14
Puoi int.Zeroinvece usarlo per renderlo felice :)
Paul Stovell,

Risposte:


90

L'effettivo modo corretto di farlo in C # è di non fare affidamento sull'ordinamento dei 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
Bene, se questo è C #, non dovresti usare ALL_CAPS per le costanti. Le costanti devono essere PascalCased - stackoverflow.com/questions/242534/...
Groky

4
@Groky: perché è importante? A chi importa come nomina le sue costanti? Questo è corretto al 100% se utilizza ALL_CAPS in modo coerente per le costanti.
marco-fiset,

4
@marcof: Beh, se le costanti fanno parte di un'interfaccia pubblica, dovrebbe seguire le linee guida per la denominazione degli Stati membri. In caso contrario, dovrebbe almeno imparare le migliori pratiche in anticipo.
Groky

2
Questo è ancora errato. Sposta il problema dall'avere un valore letterale intero (zero) in un valore letterale stringa (Crea nuovo fornitore). Nella migliore delle ipotesi il problema ora è "cosa succede se l'etichetta cambia" invece di "cosa succede se l'indice cambia".
Freiheit,

7
@Freiheit: errato. La costante di stringa qui è solo per la visualizzazione; non influisce in alcun modo sulla logica del programma. A differenza dell'utilizzo di valori / stringhe magiche per memorizzare lo stato del programma, la modifica di questa stringa (o l'aggiunta / rimozione di elementi dall'elenco) non potrebbe mai interrompere nulla.
BlueRaja - Danny Pflughoeft il

83

L'ordine nella casella combinata potrebbe cambiare. Che cosa succede se aggiungi un'altra opzione come "Crea fornitore speciale ..." prima di "Crea nuovo vendicatore ..."

Il vantaggio dell'utilizzo di una costante è che se esistono molti metodi che dipendono dall'ordine della casella combinata, è necessario modificare la costante e non tutti i metodi se ciò cambia.

L'uso di una costante è anche più leggibile di un letterale.

if (comboVendor.SelectedIndex == NewVendorIndex)

La maggior parte dei linguaggi compilati sostituirà la costante al momento della compilazione, quindi non vi è alcuna penalità per le prestazioni.


5
Userei un codice descrittivo nell'attributo value, piuttosto che fare affidamento su un indice che può cambiare (spostando l'opzione di creazione in fondo all'elenco, si romperà assolutamente il metodo costante). Quindi cerca quel codice.
CaffGeek,

1
@Chad Sono d'accordo che identificare i controlli dall'ordine in una GUI è molto fragile. Se il linguaggio supporta l'aggiunta di un valore all'elemento gui che può essere cercato, lo userei.
Michael Krussel,

3
@solution perché questa è la convenzione.
Ikke,

3
@Ikke Questa non è assolutamente la convenzione. stackoverflow.com/questions/242534/…
SolutionYogi

1
Se stiamo parlando di C #, NewVendorIndex È la convenzione. È coerente con il resto dello stile .NET.
MaR,

36

Questa situazione che descrivi è una chiamata di giudizio, personalmente non la userei se fosse usata una sola volta ed è già leggibile.

La vera risposta è che ha scelto questo per insegnarti una lezione.

Non dimenticare che è un professore, il suo compito è insegnarti la programmazione e le migliori pratiche.

Direi che sta davvero facendo un ottimo lavoro.

Sicuro che potrebbe venire un po 'assoluto ma sono sicuro che ci ripenserai prima di usare numeri magici.

Inoltre ti è entrato abbastanza per farti entrare in una comunità online di programmatori solo per scoprire quale è considerata una buona pratica in questa situazione.

Tanto di cappello al tuo professore.


+1 per indicare che, in questo caso, i mezzi giustificano il risultato finale
oliver-clare,

@ Thanos- " Non dimenticare che è un professore, il suo compito è insegnarti la programmazione e le migliori pratiche. " Questo non è mai stato il caso del mio professore. Direi che il suo compito è insegnarti ciò che il dipartimento ritiene importante una volta creato il corso.
Ramhound,

13

[...] la mia opzione principale (indice 0, che non cambia mai) apre la finestra di dialogo "Crea un nuovo fornitore" ".

Il fatto che tu abbia dovuto spiegare ciò dimostra perché dovresti usare una costante. Se introducessi una costante come NEW_VENDOR_DIALOG, il tuo codice sarebbe più autoesplicativo. Inoltre, i compilatori ottimizzano le costanti, quindi non ci saranno cambiamenti nelle prestazioni.

Scrivi programmi per programmatori, non per compilatori. A meno che tu non stia specificatamente cercando di ottimizzare il micro, cosa che non ti sembra.


2
-1 anche per citare le prestazioni. Anche se non ottimizzato, un computer può eseguire miliardi di operazioni come questa prima che l'utente se ne accorga.
Boris Yankov il

3
@Boris OP sembrava preoccupato per lo spreco di risorse, motivo per cui l'ho menzionato. Non vedo come la mia risposta sarebbe meno corretta a causa di ciò.
kba,

12

Afferma che lo 0 dovrebbe essere una costante e in realtà mi ha ancorato dei segni a causa di ciò.

Sono d'accordo. L'uso di zero qui è "magico". Immagina di leggere questo codice per la prima volta. Non sai perché lo zero è speciale e il letterale non ti dice nulla sul perché lo zero sia speciale. Se invece hai detto, if(comboVendor.SelectedIndex == CreateNewVendorIndex)allora diventa estremamente chiaro al lettore per la prima volta cosa significa il codice.

Afferma che non dovrei usare letterali nel mio codice.

Questa è una posizione estrema; una posizione realistica sarebbe quella di dire che l'uso dei letterali è una bandiera rossa che indica che il codice potrebbe non essere così chiaro come potrebbe essere. A volte è appropriato.

Il fatto è che non capisco perché vorrei rendere quel codice in quella situazione una costante. Quell'indice non cambierà mai

Che non cambierà mai è un'ottima ragione per renderlo una costante . Ecco perché le costanti sono chiamate costanti; perché non cambiano mai.

né è qualcosa che dovresti modificare.

Veramente? Non riesci a vedere alcuna situazione in cui qualcuno potrebbe voler cambiare l'ordine delle cose in una casella combinata?

Il fatto che tu possa vedere un motivo per cui questo potrebbe cambiare in futuro è un buon motivo per non renderlo una costante. Piuttosto dovrebbe essere un campo intero statico di sola lettura non costante. Una costante dovrebbe essere una quantità che è garantito per rimanere lo stesso per tutti i tempi . Pi e il numero atomico di oro sono buone costanti. I numeri di versione non lo sono; cambiano ogni versione. Il prezzo dell'oro è ovviamente una costante terribile; cambia ogni secondo. Fai solo cose costanti che non cambiano mai e poi mai .

Sembra uno spreco di memoria mantenere un singolo 0 in memoria che viene utilizzato per una situazione molto specifica e non cambia mai.

Ora veniamo al nocciolo della questione.

Questa è forse la linea più importante nella tua domanda perché indica che hai una comprensione profondamente imperfetta di (1) memoria e (2) ottimizzazione. Sei a scuola per imparare, e ora sarebbe un ottimo momento per comprendere correttamente i fondamenti. Puoi spiegare in dettaglio perché credi che "è uno spreco di memoria mantenere un singolo zero in memoria"? Prima di tutto, perché ritieni che l'ottimizzazione dell'utilizzo di quattro byte di memoria in un processo con almeno due miliardi di byte di memoria indirizzabile dall'utente sia rilevante? In secondo luogo, esattamente quale risorsa immagini viene consumata qui ? Cosa intendi con "memoria" consumata?

Sono interessato alle risposte a queste domande in primo luogo perché sono un'opportunità per te per imparare come la tua comprensione dell'ottimizzazione e della gestione della memoria non è corretta, e in secondo luogo perché voglio sempre sapere perché i principianti credono in cose bizzarre, in modo da poter progettare strumenti migliori per indurli ad avere credenze corrette.


6

Ha ragione. Hai ragione. Hai torto.

Ha ragione, concettualmente, che i numeri magici dovrebbero essere evitati. Le costanti rendono il codice più leggibile aggiungendo il contesto al significato del numero. In futuro, quando qualcuno legge il tuo codice, sa perché è stato utilizzato un determinato numero. E se hai bisogno di cambiare un valore da qualche parte lungo la linea, è molto meglio cambiarlo in un posto piuttosto che cercare di cercare ovunque un numero particolare.

Detto questo, hai ragione. In questo caso specifico, non credo davvero che una costante sia giustificata. Stai cercando il primo elemento nell'elenco, che è sempre zero. Non sarà mai 23. O -pi. In particolare stai cercando zero. Non credo davvero che tu debba ingombrare il codice rendendolo una costante.

Ti sbagli, tuttavia, nel ritenere che una costante venga trasportata come variabile, "usando la memoria". C'è una costante per l'umano e il compilatore. Indica al compilatore di inserire quel valore in quel punto durante la compilazione, dove altrimenti avresti messo un numero letterale. E anche se contenesse la costante in memoria, per tutte le applicazioni tranne le più esigenti, la perdita di efficienza non sarebbe nemmeno misurabile. Preoccuparsi dell'uso della memoria di un singolo intero rientra sicuramente nella "ottimizzazione prematura".


1
Avrei quasi votato questa risposta, se non per il terzo paragrafo, sostenendo che l'uso dello 0 letterale è abbastanza chiaro e nessuna costante è giustificata. Una soluzione molto migliore sarebbe stata quella di non fare affidamento sull'indicizzazione dell'elemento della casella combinata, ma piuttosto sul valore dell'elemento selezionato. Le costanti magiche sono costanti magiche anche se sono 0 o 3,14 o quant'altro - nominale in modo appropriato poiché ciò rende il codice più leggibile.
Roland Tepp,

Potrebbe essere meglio usare il valore nell'elenco, ma non era di questo che si trattava. La sua domanda era se quello fosse un uso appropriato per una costante. E se uno sta confrontando con 0 nel contesto dell'interfaccia con la GUI (per qualsiasi motivo - forse qualcuno sta cercando l'elemento pugno indipendentemente dal valore), penso che non sarebbe necessario usare una costante in quel punto.
GrandmasterB,

Non sarei d'accordo ... Dal solo guardare il codice, non è mai immediatamente ovvio quale sia il significato di 0 - potrebbe davvero essere che è sempre alla ricerca del primo elemento nell'elenco e basta, ma poi leggerebbe molto più pulito, se invece il confronto fosse "comboVendor.SelectedIndex == FirstIndex"?
Roland Tepp,

2

Avrei sostituito il 0con una costante per chiarire il significato, come NewVendorIndex. Non sai mai se il tuo ordine cambierà.


1

Questa è la preferenza totale del tuo professore. In genere, usi una costante solo se il valore letterale verrà utilizzato più volte, vuoi rendere ovvio al lettore quale sia lo scopo della linea o il tuo valore letterale verrà eventualmente modificato in futuro e desideri solo cambiare in un unico posto. Tuttavia, per questo semestre, il professore è il capo, quindi lo farei d'ora in poi in quella classe.

Una buona formazione per il mondo aziendale? Abbastanza possibilmente.


2
Non sono d'accordo con la parte "usa una costante solo se il valore letterale verrà usato più volte". Con 0 (e forse -1, 1) è spesso chiaro, nella maggior parte dei casi è bene dare un nome a una cosa che è molto più chiara mentre legge il codice.
johannes,

1

Ad essere onesti, anche se non credo che il tuo codice sia la migliore prassi, il suo suggerimento è francamente un po 'grottesco.

Una pratica più comune per una casella combinata .NET è di assegnare un valore vuoto all'elemento "Seleziona ..", mentre gli elementi effettivi hanno valori significativi e quindi:

if (string.IsNullOrEmpty(comboVendor.SelectedValue))

piuttosto che

if (comboVendor.SelectedIndex == 0)

3
Nel tuo esempio, null è semplicemente un altro letterale. Il cuore di questa lezione è che i letterali devono essere evitati.
Sostituito il

@overslacked: nel mio esempio non esiste alcun valore letterale nullo.
Carson63000,

Anche se esistesse un valore letterale null, null è un valore letterale accettabile da utilizzare, non credo nemmeno che ci sia un modo per verificare se un riferimento a un oggetto è null senza usare il controllo se è uguale a null.
Ramhound,

Carson63000 - Mi riferivo al tuo utilizzo di IsNullOrEmpty. Stai scambiando un "valore magico" con un altro. @Ramhound - Null è un valore letterale accettabile in alcuni casi, nessuna domanda, ma non credo che questo sia un buon esempio (usando null o blank come valore magico).
Sostituito il

-1 per "un po 'grottesco". Sebbene tu abbia ragione nel dire che usare il valore sarebbe più convenzionale e più facile da capire, se si usa
ilexexexex

1

Non ha torto nel sottolineare il valore dell'uso delle costanti e tu non hai torto nell'usare i letterali. A meno che non abbia sottolineato che questo è lo stile di codifica previsto, non si dovrebbero perdere i voti per l'uso dei letterali poiché non sono dannosi. Ho visto letterali usati dappertutto così tante volte nel codice commerciale.

Il suo punto è buono però. Questo potrebbe essere il suo modo per renderti consapevole dei vantaggi delle costanti:

1-Proteggono il codice in una certa misura da manomissioni accidentali

2-Come dice @DeadMG nella sua risposta, se lo stesso valore letterale viene utilizzato in molti punti, potrebbe apparire per errore con un valore diverso - Quindi le costanti mantengono la coerenza.

Le 3 costanti mantengono il tipo, quindi non devi usare qualcosa come 0F per indicare zero.

4-Per facilità di lettura, COBOL utilizza ZERO come parola riservata per il valore zero (ma consente anche di utilizzare lo zero letterale) - Quindi, dare un valore a un nome a volte è utile, ad esempio: (Fonte: ms-Costanti

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

o come nel tuo caso (come mostrato nella risposta di @Michael Krussel)


2
Non sarà una definizione strana per giorni alla settimana? Ci sono 7 giorni alla settimana, non 7.4287143
Winston Ewert,

@ WinstonEwert, grazie per il tuo commento. 365/52 = 7.01923076923077 = 7 + (1/52). Ora, se si rimuove la parte frazionaria e si calcola 7 * 52 si ottengono 364 giorni, che non è il numero corretto di giorni in un anno. Avere una frazione è più preciso che perderlo (dal momento che è possibile formattare il risultato per mostrare 7 se si desidera solo visualizzare il numero). Ad ogni modo, era solo un esempio di MS sulle costanti, ma il tuo punto è interessante.
NoChance,

1
Naturalmente è solo un esempio, ma mi oppongo alla tua affermazione che è più preciso includere la frazione. Una settimana è definita come 7 giorni. Data la tua definizione, 26 settimane sono 182,5 giorni che semplicemente non sono accurati. Davvero, il problema è tuo int weeks = 52, non ci sono 52 settimane all'anno. Ci sono 52.142857142857146 settimane in un anno e questo è il numero su cui dovresti tenere le frazioni. Ovviamente, l'unica cosa che è effettivamente costante in quell'intero insieme di costanti è il numero di mesi.
Winston Ewert,

0

Dovresti metterlo in una costante solo se ha una derivazione complessa o se si ripete spesso. Altrimenti, un letterale va bene. Mettere tutto in una costante è un eccesso totale.


Solo se si considera due volte spesso e non è mai necessario modificare il codice. È davvero facile creare bug modificando uno dei due casi.
BillThor il

0

In realtà, come detto, cosa succede se la sua posizione cambia? Quello che potresti / dovresti fare è usare un codice, piuttosto che fare affidamento sull'indice.

Quindi, quando crei la tua lista di selezione, finisce con html come

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

Quindi, anziché verificare selectedIndex === 0, verificare che il valore sia CREATECODEquale sarebbe in una costante e sarebbe stato utilizzato sia per questo test, sia durante la creazione dell'elenco di selezione.


3
Probabilmente un buon approccio, ma visto che sta usando C #, html non è il codice di esempio più promettente.
Winston Ewert,

0

Me ne sbarazzerei del tutto. Basta mettere un nuovo pulsante Crea accanto all'elenco della casella combinata. Fare doppio clic su un elemento nell'elenco per modificarlo o fare clic sul pulsante. Non avere le nuove funzionalità sepolte nella casella combinata. Quindi il numero magico viene rimosso del tutto.

In generale qualsiasi numero letterale nel codice dovrebbe essere definito come costante in modo da mettere il contesto attorno al numero. Cosa significa zero? In questo caso 0 = NEW_VENDOR. In altri casi, potrebbe significare qualcosa di diverso, quindi è sempre una buona idea per la leggibilità e la manutenibilità mettere un po 'di contesto attorno ad esso.


0

Come altri hanno già detto, è necessario utilizzare un metodo diverso dal numero indice per identificare l'elemento della casella combinata che corrisponde a una determinata azione; oppure puoi trovare l'indice con qualche logica programmatica e memorizzarlo in una variabile.

Il motivo per cui sto scrivendo è di rispondere al tuo commento sull'uso della memoria. In C #, come nella maggior parte delle lingue, le costanti vengono "piegate" dal compilatore. Ad esempio, compilare il seguente programma ed esaminare IL. Scoprirai che tutti quei numeri non arrivano nemmeno all'IL, per non parlare della memoria del computer:

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 risultante:

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

Quindi, sia che si usi una costante, un letterale o un kilobyte di codice usando l'aritmetica costante, il valore viene trattato letteralmente nell'IL.

Un punto correlato: la piegatura costante si applica ai valori letterali delle stringhe. Molti credono che una chiamata come questa causi una concatenazione di stringhe troppo inutile e inefficiente:

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

Ma dai un'occhiata a 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

In conclusione: gli operatori su espressioni costanti generano espressioni costanti e il compilatore esegue tutto il calcolo; non influisce sulle prestazioni di runtime.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.