Sollte eine Aufzählung mit einer 0 oder einer 1 beginnen?

136

Stellen Sie sich vor, ich habe die folgende Aufzählung definiert:

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

Was ist die beste Vorgehensweise bei der Verwendung von Enum? Sollte es 1wie im obigen Beispiel beginnen oder mit 0(ohne die expliziten Werte) wie folgt beginnen:

public enum Status : byte
{
    Inactive,
    Active
}
Acaz Souza
quelle
13
Müssen Sie sie wirklich überhaupt explizit nummerieren?
Yuck
164
Aufzählungen wurden nur erstellt, damit solche Dinge nicht wichtig sind.
BoltClock
9
@ Daniel - arrgh nein! Es ist besser, eine Aufzählung zu verwenden, wenn Sie glauben, dass ein Boolescher Wert ausreicht, als einen Booleschen Wert zu verwenden, wenn Sie an eine Aufzählung denken .
AAT
22
@ Daniel wegen des FileNotFound-Wertes natürlich
Joubarc
5
xkcd.com/163 gilt für Enum noch besser als für Array-Indizes.
links um den

Antworten:

161

Richtlinien für das Framework-Design :

✔️ Geben Sie bei einfachen Aufzählungen den Wert Null an.

Erwägen Sie, den Wert so etwas wie "Keine" zu nennen. Wenn ein solcher Wert für diese bestimmte Aufzählung nicht geeignet ist, sollte dem häufigsten Standardwert für die Aufzählung der zugrunde liegende Wert Null zugewiesen werden.

Framework Design Guidelines / Designing Flag Enums :

❌ Vermeiden Sie die Verwendung von Flag-Enum-Werten von Null, es sei denn, der Wert steht für "Alle Flags sind gelöscht" und wird entsprechend benannt, wie in der nächsten Richtlinie vorgeschrieben.

✔️ Nennen Sie den Nullwert der Flag-Aufzählungen None. Für eine Flag-Aufzählung muss der Wert immer "Alle Flags werden gelöscht" bedeuten.

Andrey Taptunov
quelle
28
Frühzeitig fehlschlagen: Wenn "Keine" nicht geeignet ist, aber kein logischer Standardwert vorhanden ist, würde ich immer noch den Wert Null setzen (und ihn "Keine" oder "Ungültig" nennen), der nicht für die Verwendung vorgesehen ist Wenn ein Klassenmitglied dieser Aufzählung nicht richtig initialisiert wird, kann der nicht initialisierte Wert leicht erkannt werden, und in switchAnweisungen springt er zu dem defaultAbschnitt, in den ich a werfe InvalidEnumArgumentException. Andernfalls wird ein Programm möglicherweise unbeabsichtigt mit dem Nullwert einer Aufzählung weiter ausgeführt, was gültig sein und unbemerkt bleiben kann.
Allon Guralnek
1
@Allon Es scheint besser, nur die Enum-Werte anzugeben, von denen Sie wissen, dass sie gültig sind, und dann im Setter und / oder Konstruktor nach ungültigen Werten zu suchen. Auf diese Weise wissen Sie sofort, ob ein Code nicht richtig funktioniert, anstatt zuzulassen, dass ein Objekt mit ungültigen Daten für eine unbekannte Zeit existiert, und erfahren es später. Sofern 'Keine' keinen gültigen Status darstellt, sollten Sie ihn nicht verwenden.
wprl
@SoloBold: Das klingt nach einem Fall, in dem Sie nicht vergessen, ein Enum-Klassenmitglied in einem Konstruktor zu initialisieren. Keine Validierungsmenge hilft Ihnen, wenn Sie die Initialisierung oder die Validierung vergessen. Es gibt auch einfache DTO-Klassen, die keine Konstruktoren haben, sondern stattdessen auf Objektinitialisierern basieren . Die Jagd nach einem solchen Käfer kann äußerst schmerzhaft sein. Das Hinzufügen eines nicht verwendeten Aufzählungswerts führt jedoch zu einer hässlichen API. Ich würde es für APIs vermeiden, die auf den öffentlichen Verbrauch ausgerichtet sind.
Allon Guralnek
@Allon Das ist ein guter Punkt, aber ich würde argumentieren, dass Sie Aufzählungen in der Setter-Funktion validieren und vom Getter werfen sollten, wenn der Setter nie aufgerufen wurde. Auf diese Weise haben Sie einen Fehlerpunkt und können planen, dass das Feld immer einen gültigen Wert hat, was das Design und den Code vereinfacht. Meine zwei Cent sowieso.
wprl
@SoloBold: Stellen Sie sich eine DTO-Klasse mit 15 automatisch implementierten Eigenschaften vor. Sein Körper ist 15 Zeilen lang. Stellen Sie sich nun dieselbe Klasse mit regulären Eigenschaften vor. Das sind mindestens 180 Zeilen, bevor eine Überprüfungslogik hinzugefügt wird. Diese Klasse wird ausschließlich intern nur für Datenübertragungszwecke verwendet. Welche würden Sie lieber beibehalten, eine Klasse mit 15 Zeilen oder eine Klasse mit mehr als 180 Zeilen? Prägnanz hat ihren Wert. Trotzdem sind unsere beiden Stile korrekt, sie sind einfach unterschiedlich. (Ich denke, hier kommt AOP ins Spiel und gewinnt beide Seiten des Arguments).
Allon Guralnek
66

Nun, ich glaube, ich bin mit den meisten Antworten nicht einverstanden, die besagen, sie nicht explizit zu nummerieren. Ich nummeriere sie immer explizit, aber das liegt daran, dass ich sie in den meisten Fällen in einem Datenstrom speichere, in dem sie als ganzzahliger Wert gespeichert sind. Wenn Sie die Werte nicht explizit hinzufügen und dann einen neuen Wert hinzufügen, können Sie die Serialisierung unterbrechen und dann alte persistierte Objekte nicht genau laden. Wenn Sie diese Werte dauerhaft speichern möchten, würde ich dringend empfehlen, die Werte explizit festzulegen.

pstrjds
quelle
9
+1, vereinbart, aber nur für den Fall, dass Ihr Code aus einem externen Grund (z. B. Serialisierung) auf der Ganzzahl basiert. Überall sonst sollten Sie sich daran halten, das Framework seine Arbeit machen zu lassen. Wenn Sie sich intern auf die ganzzahligen Werte verlassen, machen Sie wahrscheinlich etwas falsch (siehe: Lassen Sie das Framework seinen Job machen).
Matthew Scharley
3
Ich mag es, den Text der Aufzählung beizubehalten. Macht die Datenbank imo weitaus benutzerfreundlicher.
Dave
2
Normalerweise müssen Sie die Werte nicht explizit festlegen ... auch wenn sie serialisiert werden. Fügen Sie am Ende IMMER neue Werte hinzu. Dadurch werden Serialisierungsprobleme gelöst. Andernfalls benötigen Sie möglicherweise eine Versionierung Ihres Datenspeichers (z. B. einen Dateikopf mit einer Version zum Ändern des Verhaltens beim Lesen / Schreiben von Werten) (oder siehe Erinnerungsmuster)
Beachwalker,
4
@ Dave: Wenn Sie nicht explizit dokumentieren, dass Aufzählungstexte heilig sind, stellen Sie sich auf einen Fehler ein, wenn ein zukünftiger Programmierer beschließt, einen Namen klarer anzupassen oder einer Namenskonvention zu entsprechen.
Supercat
1
@pstrjds: Ich denke, es ist ein stilistischer Kompromiss - Speicherplatz ist jedoch billig, die Zeit, die für das ständige Konvertieren zwischen Enum-Werten und das Durchsuchen der Datenbank aufgewendet wird, ist relativ teuer (doppelt, wenn Sie ein Berichterstellungstool für eine Datenbank oder ähnliches eingerichtet haben). . Wenn Sie sich Sorgen um Speicherplatz machen, können Sie mit den neueren SQL Server-Versionen eine Datenbank komprimieren lassen, was bedeutet, dass 1000 Vorkommen von "SomeEnumTextualValue" kaum mehr Speicherplatz belegen. Natürlich funktioniert dies nicht bei allen Projekten - es ist ein Kompromiss. Ich denke, die Sorge um die Bandbreite riecht vielleicht nach vorzeitiger Optimierung!
Dave
15

Eine Aufzählung ist ein Werttyp und ihr Standardwert (z. B. für ein Aufzählungsfeld in einer Klasse) ist 0, wenn er nicht explizit initialisiert wird.

Daher möchten Sie im Allgemeinen 0 als definierte Konstante haben (z. B. Unbekannt).

Wenn Sie in Ihrem Beispiel Inactivedie Standardeinstellung sein möchten , sollte sie den Wert Null haben. Andernfalls sollten Sie eine Konstante hinzufügen Unknown.

Einige Leute haben empfohlen, dass Sie keine expliziten Werte für Ihre Konstanten angeben. Wahrscheinlich in den meisten Fällen ein guter Rat, aber es gibt einige Fälle, in denen Sie dies tun möchten:

  • Flaggen Aufzählungen

  • Aufzählungen, deren Werte im Interop mit externen Systemen (z. B. COM) verwendet werden.

Joe
quelle
Ich habe festgestellt, dass Flags-Enums viel besser lesbar sind, wenn Sie die Werte nicht explizit festlegen. - Auch viel weniger fehleranfällig, damit der Compiler die binäre Mathematik für Sie erledigt. (dh [Flags] enum MyFlags { None = 0, A, B, Both = A | B, /* etc. */ }ist viel besser lesbar als [Flags] enum MyFlags { None = 0, A = 1, B = 2, Both = 3, /* etc */ }.)
BrainSlugs83
1
@ BrainSlugs83 - Ich sehe nicht, wie das im allgemeinen Fall hilfreich wäre - [Flags] enum MyFlags { None=0, A, B, C } würde z. B. dazu führen [Flags] enum MyFlags { None=0, A=1, B=2, C=3 }, während Sie für eine Flags-Aufzählung normalerweise C = 4 wünschen würden.
Joe
14

Wenn Sie keinen bestimmten Grund haben, dies zu ändern, lassen Sie die Aufzählungen mit ihren Standardwerten, die bei Null beginnen.

public enum Status : byte
{
    Inactive,
    Active
}
FishBasketGordo
quelle
6

Ich würde sagen, die beste Vorgehensweise ist, sie nicht zu nummerieren und implizit zu lassen - was bei 0 beginnen würde. Da es implizit ist, ist es die Sprachpräferenz, die immer gut zu befolgen ist :)

John Humphreys - w00te
quelle
6

Ich würde eine Aufzählung vom Typ Boolean mit einer 0 beginnen.

Es sei denn, "Inative" bedeutet etwas anderes als "Inaktiv" :)

Dies behält den Standard für diese bei.

Mark Schultheiss
quelle
6

Ich würde sagen, es hängt davon ab, wie Sie sie verwenden. Für das Markieren von Enum ist es eine gute Praxis, 0 für den NoneWert zu haben , wie folgt :

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

Wenn Ihre Aufzählung wahrscheinlich einer Datenbank-Nachschlagetabelle zugeordnet wird, würde ich sie mit 1 beginnen. Für professionell geschriebenen Code sollte dies nicht viel bedeuten, dies verbessert jedoch die Lesbarkeit.

In anderen Fällen würde ich es so lassen, wie es ist, ohne darauf zu achten, ob sie mit 0 oder 1 beginnen.

Michael Sagalovich
quelle
5

Sofern Sie keinen guten Grund haben, die Rohwerte zu verwenden, sollten Sie immer nur implizite Werte verwenden und diese mit Status.Activeund referenzieren Status.Inactive.

Der Haken ist, dass Sie möglicherweise Daten in einer Flatfile oder DB speichern oder eine Flatfile oder DB verwenden möchten, die von einer anderen Person erstellt wurde. Wenn Sie es selbst machen, machen Sie es so, dass die Nummerierung zu dem passt, wofür die Aufzählung verwendet wird.

Wenn die Daten nicht Ihnen gehören, möchten Sie natürlich alles verwenden, was der ursprüngliche Entwickler als Nummerierungsschema verwendet hat.

Wenn Sie die Aufzählung als Flaggensatz verwenden möchten, gibt es eine einfache Konvention, die es wert ist, befolgt zu werden:

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
}

Die Werte sollten Zweierpotenzen sein und können mit Bitverschiebungsoperationen ausgedrückt werden. Nonesollte natürlich sein 0, ist aber Allweniger offensichtlich -1. ~0ist die binäre Negation von 0und führt zu einer Zahl, auf die jedes Bit gesetzt ist 1, was einen Wert von darstellt-1 . Für zusammengesetzte Flags (häufig zur Vereinfachung verwendet) können andere Werte mit dem bitweisen Operator oder dem Operator zusammengeführt werden |.

zzzzBov
quelle
3

Weisen Sie keine Nummern zu. Verwenden Sie es einfach so, wie es verwendet werden soll.

Fusel
quelle
3

Wenn nicht angegeben, beginnt die Nummerierung bei 0.

Es ist wichtig, explizit zu sein, da Aufzählungen häufig serialisiert und als int und nicht als Zeichenfolge gespeichert werden.

Für jede in der Datenbank gespeicherte Aufzählung nummerieren wir die Optionen immer explizit, um eine Verschiebung und Neuzuweisung während der Wartung zu verhindern.

Laut Microsoft wird empfohlen, die erste Nulloption zu verwenden, um einen nicht initialisierten oder den häufigsten Standardwert darzustellen.

Unten finden Sie eine Verknüpfung zum Starten der Nummerierung bei 1 anstelle von 0.

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

Wenn Sie Flag-Werte festlegen möchten, um Bitoperatoren für Enum-Werte zu verwenden, beginnen Sie nicht mit der Nummerierung beim Nullwert.

dru
quelle
2

Wenn Sie bei 1 beginnen, können Sie leicht Ihre Sachen zählen.

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

Wenn Sie bei 0 beginnen, verwenden Sie den ersten als Wert für nicht initialisierte Dinge.

{
    BOX_NO_THING   = 0,
    BOX_THING1     = 1,
    BOX_THING2     = 2,
    BOX_NUM_THING  = BOX_THING2
};
Jonathan Cline IEEE
quelle
5
Entschuldigung, Jonathan. Ich denke, dieser Vorschlag ist für mich eine kleine "alte Schule" (eine Art Konvention stammt aus Jahren mit niedrigem Niveau). Dies ist als schnelle Lösung zum "Einbetten" einiger zusätzlicher Informationen zur Aufzählung in Ordnung, in größeren Systemen ist dies jedoch keine gute Vorgehensweise. Sie sollten keine Aufzählung verwenden, wenn Sie Informationen über die Anzahl der verfügbaren Werte usw. benötigen. Und was ist mit einer BOX_NO_THING1? Würden Sie ihm BOX_NO_THING + 1 geben? Aufzählungen sollten als das verwendet werden, wofür sie verwendet werden sollen: Bestimmte (int) Werte, die durch "sprechende" Namen dargestellt werden.
Beachwalker
Hm. Sie nehmen an, dass es alte Schule ist, weil ich alle Großbuchstaben verwendet habe, anstatt MicrosoftBumpyCaseWithLongNames. Obwohl ich damit einverstanden bin, ist es besser, Iteratoren als Schleifen zu verwenden, bis eine aufgezählte XyzNumDefsInMyEnum-Definition erreicht ist.
Jonathan Cline IEEE
Dies ist in vielerlei Hinsicht eine schreckliche Praxis in C #. Wenn Sie nun Ihre Aufzählungen auf die richtige Weise zählen oder versuchen, sie auf die richtige Weise aufzulisten, erhalten Sie ein zusätzliches, doppeltes Objekt. Außerdem wird der Aufruf von .ToString () unter anderem möglicherweise mehrdeutig (was die moderne Serialisierung vermasselt).
BrainSlugs83
0

Erstens, es sei denn, Sie geben bestimmte Werte aus einem bestimmten Grund an (der numerische Wert hat eine andere Bedeutung, z. B. die Datenbank oder ein externer Dienst), geben Sie zunächst überhaupt keine numerischen Werte an und lassen Sie sie explizit sein.

Zweitens sollten Sie immer ein Element mit dem Wert Null haben (in Nicht-Flags-Aufzählungen). Dieses Element wird als Standardwert verwendet.

Justin Niessner
quelle
0

Beginnen Sie sie nicht bei 0, es sei denn, es gibt einen Grund dafür, z. B. die Verwendung als Indizes für ein Array oder eine Liste, oder wenn es einen anderen praktischen Grund gibt (z. B. die Verwendung in bitweisen Operationen).

Sie enumsollten genau dort beginnen, wo es sein muss. Es muss auch nicht sequentiell sein. Wenn die Werte explizit festgelegt werden, müssen sie eine semantische Bedeutung oder praktische Überlegungen widerspiegeln. Zum Beispiel sollte eine enumder "Flaschen an der Wand" von 1 bis 99 nummeriert sein, während eine enumfür Potenzen von 4 wahrscheinlich bei 4 beginnen und mit 16, 64, 256 usw. fortfahren sollte.

Darüber hinaus sollte das Hinzufügen eines Elements mit dem Wert Null enumnur erfolgen, wenn es einen gültigen Status darstellt. Manchmal sind "keine", "unbekannt", "fehlend" usw. gültige Werte, aber oft nicht.

wprl
quelle
-1

Ich möchte meine Aufzählungen bei 0 beginnen, da dies die Standardeinstellung ist, aber ich möchte auch einen unbekannten Wert mit dem Wert -1 einfügen. Dies wird dann zur Standardeinstellung und kann manchmal beim Debuggen helfen.

Tomas McGuinness
quelle
4
Schreckliche Idee. Als Wertetyp werden Aufzählungen immer auf Null initialisiert. Wenn Sie einen Wert haben möchten, der unbekannt oder nicht initialisiert ist, muss er 0 sein. Sie können den Standardwert nicht auf -1 ändern. Die Nullfüllung ist in der gesamten CLR fest codiert.
Ben Voigt
Ah, das habe ich nicht bemerkt. Ich setze im Allgemeinen den Wert eines Enum-Werts / einer Enum-Eigenschaft, wenn ich dekaliere / initialisiere. Danke für den Zeiger.
Tomas McGuinness