Un Enum dovrebbe iniziare con uno 0 o un 1?


136

Immagina di aver definito il seguente Enum:

public enum Status : byte
{
    Inactive = 1,
    Active = 2,
}

Qual è la migliore pratica per usare enum? Dovrebbe iniziare 1come nell'esempio precedente o iniziare 0(senza i valori espliciti) in questo modo:

public enum Status : byte
{
    Inactive,
    Active
}

13
Hai davvero bisogno di numerarli esplicitamente?
Yuck,

164
Gli enum sono stati creati proprio in modo che cose come questa non sarebbero importanti.
BoltClock

9
@Daniel - arrgh no! Meglio usare un enum quando pensi che un booleano farà piuttosto che usare un booleano quando stai pensando a un enum.
AAT

22
@Daniel a causa del valore FileNotFound, ovviamente
Joubarc

5
xkcd.com/163 si applica all'enum anche meglio degli array di indici.
lasciato circa il

Risposte:


161

Linee guida per la progettazione del framework :

✔️ Fornisce un valore pari a zero su enumerazioni semplici.

Valuta di chiamare il valore in modo simile a "Nessuno". Se tale valore non è appropriato per questo particolare enum, al valore predefinito più comune per l'enum dovrebbe essere assegnato il valore sottostante pari a zero.

Linee guida per la progettazione di quadri / Progettazione di enumerazioni di bandiere :

❌ EVITARE di utilizzare valori di enum di flag pari a zero, a meno che il valore non rappresenti "vengono cancellati tutti i flag" e sia denominato in modo appropriato, come prescritto dalla linea guida successiva.

✔️ DO nome il valore zero di enumerazioni bandiera Nessuno. Per un enum di flag, il valore deve sempre significare "tutte le flag vengono cancellate".


28
Fallimento anticipato: se "Nessuno" non è appropriato ma non esiste un valore logico predefinito, metterei comunque un valore pari a zero (e lo chiamerei "Nessuno" o "Non valido") che non è destinato a essere utilizzato, solo per se un membro della classe di quella enumerazione non è inizializzato correttamente, il valore non inizializzato può essere facilmente individuato e nelle switchistruzioni passerà alla defaultsezione, dove lancio a InvalidEnumArgumentException. Altrimenti un programma potrebbe continuare involontariamente a funzionare con il valore zero di un elenco, che può essere valido e passare inosservato.
Allon Guralnek,

1
@Allon Sembra meglio fornire solo i valori enum che sai essere validi e quindi verificare la presenza di valori non validi nel setter e / o nel costruttore. In questo modo sai immediatamente se un po 'di codice non funziona correttamente piuttosto che consentire a un oggetto con dati non validi di esistere per un tempo sconosciuto e scoprirlo in seguito. A meno che "Nessuno" rappresenti uno stato valido, non dovresti usarlo.
wprl,

@SoloBold: Sembra un caso in cui non dimenticare di inizializzare un membro della classe enum in un costruttore. Nessun importo di convalida ti aiuterà se dimentichi di inizializzare o dimentichi di convalidare. Inoltre, ci sono semplici classi DTO che non hanno costruttori, ma si basano invece su inizializzatori di oggetti . Cercare un tale insetto può essere estremamente doloroso. Tuttavia, l'aggiunta di un valore di enumerazione inutilizzato rende un'API brutta. Lo eviterei per le API orientate al consumo pubblico.
Allon Guralnek,

@Allon Questo è un buon punto, ma direi che dovresti convalidare gli enum nella funzione setter e lanciare dal getter se il setter non è mai stato chiamato. In questo modo hai un punto di errore e puoi pianificare sul campo sempre un valore valido, il che semplifica il design e il codice. I miei due centesimi comunque.
sett

@SoloBold: immagina una classe DTO con 15 proprietà implementate automaticamente. Il suo corpo è lungo 15 linee. Ora immagina quella stessa classe con proprietà regolari. Sono almeno 180 righe prima di aggiungere qualsiasi logica di verifica. Questa classe è utilizzata esclusivamente internamente ai soli fini del trasferimento dei dati. Quale preferiresti mantenere, una classe di 15 righe o una classe di 180+ righe? La succinta ha il suo valore. Tuttavia, entrambi i nostri stili sono corretti, sono semplicemente diversi. (Immagino che questo sia il punto in cui l'AOP si diffonde e vince entrambe le parti della discussione).
Allon Guralnek,

66

Bene, immagino di essere in disaccordo con la maggior parte delle risposte che dicono di non numerarle esplicitamente. Li scrivo sempre in modo esplicito, ma ciò accade perché nella maggior parte dei casi finisco per persistere in un flusso di dati in cui sono memorizzati come valore intero. Se non si aggiungono esplicitamente i valori e quindi si aggiunge un nuovo valore, è possibile interrompere la serializzazione e non essere in grado di caricare con precisione vecchi oggetti persistenti. Se hai intenzione di fare qualsiasi tipo di archivio persistente di questi valori, ti consiglio vivamente di impostare esplicitamente i valori.


9
+1, concordato, ma solo nel caso in cui il codice si basi sull'intero per qualche motivo esterno (es. Serializzazione). Ovunque altro dovresti limitarti a lasciare che il framework faccia il suo lavoro. Se ti affidi ai valori interi internamente, probabilmente stai facendo qualcosa di sbagliato (vedi: lascia che il framework faccia il suo lavoro).
Matthew Scharley,

3
Mi piace persistere il testo dell'enum. Rende il database molto più utilizzabile imo.
Dave,

2
Normalmente, non è necessario impostare esplicitamente i valori ... anche quando vengono serializzati. basta aggiungere SEMPRE nuovi valori alla fine. questo risolverà i problemi di serializzazione. altrimenti potresti aver bisogno di un controllo delle versioni del tuo archivio dati (ad es. un'intestazione di file contenente una versione per modificare il comportamento durante la lettura / scrittura dei valori) (o vedi lo schema del ricordo)
Beachwalker

4
@Dave: a meno che tu non dichiari esplicitamente che i testi enum sono sacri, ti prepari al fallimento se un futuro programmatore decide di modificare un nome in modo che sia più chiaro o conforme a qualche convenzione di denominazione.
supercat

1
@pstrjds: immagino che sia un compromesso stilistico - lo spazio su disco è economico, il tempo speso costantemente per la conversione tra i valori enum e una ricerca nel db è relativamente costoso (doppiamente quindi se si dispone di uno strumento di reporting impostato su un database o qualcosa di simile) . Se sei preoccupato per lo spazio, con le versioni più recenti di SQL Server puoi avere un database compresso, il che significa che 1000 occorrenze di "SomeEnumTextualValue" useranno appena più spazio. Ovviamente questo non funzionerà per tutti i progetti - è un compromesso. Penso che la preoccupazione per la larghezza di banda abbia un odore di ottimizzazione prematura, forse!
Dave,

15

Un Enum è un tipo di valore e il suo valore predefinito (ad esempio per un campo Enum in una classe) sarà 0 se non inizializzato esplicitamente.

Pertanto in genere si desidera avere 0 come costante definita (ad es. Sconosciuto).

Nel tuo esempio, se vuoi Inactiveessere il valore predefinito, dovrebbe avere il valore zero. Altrimenti potresti considerare di aggiungere una costante Unknown.

Alcune persone hanno raccomandato di non specificare esplicitamente i valori per le costanti. Probabilmente un buon consiglio nella maggior parte dei casi, ma ci sono alcuni casi in cui vorrai farlo:

  • Flag enums

  • Enum i cui valori sono usati in interoperabilità con sistemi esterni (es. COM).


Ho scoperto che i flag enum sono molto più leggibili quando non si impostano esplicitamente i valori. - Anche molto meno soggetto a errori per consentire al compilatore di fare la matematica binaria per te. (cioè [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }è molto più leggibile, di [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ }.)
BrainSlugs83

1
@ BrainSlugs83 - Non vedo in che modo sarebbe utile nel caso generale - ad es. [Flags] enum MyFlags { None=0, A, B, C } Ne deriverebbe un risultato [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, mentre per un enum Flags normalmente vorresti C = 4.
Joe,

14

A meno che tu non abbia un motivo specifico per cambiarlo, lascia enum con i loro valori predefiniti, che iniziano da zero.

public enum Status : byte
{
    Inactive,
    Active
}

6

Direi che la migliore pratica è non numerarli e lasciare che sia implicito - che inizierebbe da 0. Dal momento che è implicito è la preferenza linguistica che è sempre buona seguire :)


6

Vorrei iniziare un enum di tipo booleano con uno 0.

A meno che "Inativo" significhi qualcosa di diverso da "Inattivo" :)

Questo mantiene lo standard per quelli.


6

Direi che dipende da come li usi. Per segnalare l'enumerazione è buona norma avere 0 per Nonevalore, come quello:

[Flags]
enum MyEnum
{
    None = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    All = Option1 | Option2 | Option3,
}

Quando è probabile che il tuo enum sia mappato su una tabella di ricerca del database, lo inizierei con 1. Non dovrebbe importare molto per il codice scritto professionalmente, ma questo migliora la leggibilità.

In altri casi lo lascerei così com'è, non importa se iniziano con 0 o 1.


5

A meno che tu non abbia una buona ragione per usare i valori grezzi, dovresti sempre e solo usare valori impliciti e fare riferimento a loro con Status.Activee Status.Inactive.

Il problema è che potresti voler archiviare i dati in un file flat o DB, oppure utilizzare un file flat o DB creato da qualcun altro. Se lo fai tu stesso, fallo in modo che la numerazione si adatti a ciò per cui Enum è usato.

Se i dati non sono tuoi, ovviamente vorrai usare qualunque cosa il dev originale abbia usato come schema di numerazione.

Se stai pensando di usare l'Enum come un set di flag, c'è una semplice convenzione che vale la pena seguire:

enum Example
{
  None      = 0,            //  0
  Alpha     = 1 << 0,       //  1
  Beta      = 1 << 1,       //  2
  Gamma     = 1 << 2,       //  4
  Delta     = 1 << 3,       //  8
  Epsilon   = 1 << 4,       // 16
  All       = ~0,           // -1
  AlphaBeta = Alpha | Beta, //  3
}

I valori dovrebbero essere potenze di due e possono essere espressi usando operazioni di bit-shift. None, ovviamente dovrebbe essere 0, ma Allè meno ovviamente -1. ~0è la negazione binaria di 0e risulta in un numero su cui è impostato ogni bit 1, che rappresenta un valore di-1 . Per i flag composti (spesso usati per comodità) altri valori possono essere uniti usando bit per bit o operatore |.


3

Non assegnare alcun numero. Usalo come dovrebbe essere usato.


3

Se non specificato, la numerazione inizia da 0.

È importante essere espliciti poiché gli enum sono spesso serializzati e memorizzati come int, non come stringa.

Per ogni enum archiviato nel database, numeriamo sempre in modo esplicito le opzioni per impedire spostamenti e riassegnazioni durante la manutenzione.

Secondo Microsoft, la convenzione consigliata è utilizzare la prima opzione zero per rappresentare un valore predefinito non inizializzato o il più comune.

Di seguito è riportato un collegamento per iniziare la numerazione da 1 invece di 0.

public enum Status : byte
{
    Inactive = 1,
    Active
}

Se si desidera impostare i valori flag per utilizzare gli operatori bit sui valori enum, non iniziare la numerazione sul valore zero.


2

Se inizi da 1, puoi facilmente ottenere un conteggio delle tue cose.

{
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

Se inizi da 0, usa il primo come valore per le cose non inizializzate.

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};

5
Scusa, Jonathan. Penso che questo suggerimento sia un po '"vecchia scuola" nella mia mente (il tipo di convenzione viene da anni di basso livello). Va bene come soluzione rapida per "incorporare" alcune informazioni aggiuntive sull'enum ma questa non è una buona pratica in sistemi più grandi. Non dovresti usare un enum se hai bisogno di informazioni sul numero di valori disponibili ecc. E che dire di un BOX_NO_THING1? Gli daresti BOX_NO_THING + 1? Gli enum dovrebbero essere usati per quello per cui sono destinati: valori specifici (int) rappresentati da nomi "parlanti".
Beachwalker,

Hm. Supponi che sia una vecchia scuola perché ho usato tutte le protezioni, suppongo, piuttosto che MicrosoftBumpyCaseWithLongNames. Anche se sono d'accordo, è meglio usare iteratori che loop fino a raggiungere una definizione XyzNumDefsInMyEnum enum'ed.
Jonathan Cline IEEE

Questa è una pratica terribile in C # in vari modi. Ora, quando vai a ottenere un conteggio dei tuoi enum nel modo corretto, o se provi a enumerarli nel modo corretto, otterrai un oggetto duplicato extra. Inoltre rende la chiamata .ToString () potenzialmente ambigua (rovinando la serializzazione moderna), tra le altre cose.
BrainSlugs83

0

Prima di tutto, a meno che non si specifichino valori specifici per un motivo (il valore numerico ha significato altrove, ad esempio il database o il servizio esterno), non specificare affatto valori numerici e lasciarli espliciti.

In secondo luogo, dovresti sempre avere un oggetto di valore zero (in enumerazioni senza flag). Tale elemento verrà utilizzato come valore predefinito.


0

Non avviarli su 0 a meno che non ci sia una ragione per, come usarli come indici in un array o in una lista, o se c'è qualche altro motivo pratico (come usarli in operazioni bit a bit).

Il tuo enumdovrebbe iniziare esattamente dove è necessario. Non deve essere neppure sequenziale. I valori, se impostati esplicitamente, devono riflettere un significato semantico o una considerazione pratica. Ad esempio, una enumdelle "bottiglie sul muro" dovrebbe essere numerata da 1 a 99, mentre una enumper i poteri di 4 dovrebbe probabilmente iniziare da 4 e continuare con 16, 64, 256, ecc.

Inoltre, aggiungere un elemento a valore zero a enumdovrebbe essere fatto solo se rappresenta uno stato valido. A volte "nessuno", "sconosciuto", "mancante" ecc. Sono valori validi, ma molte volte non lo sono.


-1

Mi piace iniziare le mie enumerazioni su 0, dato che è l'impostazione predefinita, ma mi piace anche includere un valore Unknown, con un valore -1. Questo diventa quindi il valore predefinito e può aiutare a volte con il debug.


4
Idea orribile. Come tipo di valore, gli enum sono sempre inizializzati su zero. Se stai per avere un valore che rappresenta sconosciuto o non inizializzato, deve essere 0. Non puoi cambiare il valore predefinito in -1, il riempimento zero è hardcoded in tutto il CLR.
Ben Voigt,

Ah, non me ne sono reso conto. In genere imposto il valore di un valore / proprietà enum quando decalro / inizializzo. Grazie per il puntatore.
Tomas McGuinness,
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.