Ich habe heute versehentlich eine Aufzählung definiert, die keine Werte enthielt. Wie dieser zum Beispiel:
public enum MyConfusingEnum{}
Der Compiler war sehr froh, dass ich das definieren und den Code erfolgreich erstellen konnte.
Jetzt kann ich das offensichtlich nicht im herkömmlichen Sinne verwenden, da der Code, ..
var mySadCompiler = MyConfusingEnum;
keinen Wert angeben , aber interessanterweise ich war die Lage zu sagen, ..
var myRoundTheHousesZeroState = Activator.CreateInstance<MyConfusingEnum>();
was, wie ich angedeutet habe, ein Werttyp MyConfusingEnum
mit dem Wert 0 ist;
Meine Frage ist, warum der Compiler eine leere Definition zulässt und gibt es Szenarien, in denen dies nützlich sein könnte?
c#
compiler-construction
enums
EightyOne Unite
quelle
quelle
var x = (MyConfusingEnum)0;
var x = (MyConfusingEnum)99
.Antworten:
Zunächst einmal hätten Sie das viel einfacher tun können:
MyConfusingEnum x1 = 0; MyConfusingEnum x2 = default(MyConfusingEnum); MyConfusingEnum x3 = new MyConfusingEnum(); MyConfusingEnum x4 = (MyConfusingEnum) 123;
Alle oben genannten funktionieren gut. (Sie werden überrascht sein, dass das erste funktioniert. Weitere Informationen finden Sie im Abschnitt mit den Spezifikationen für implizite Aufzählungskonvertierungen.)
Ich beantworte Ihre Frage zunächst mit einer Frage. Würden Sie auch den Compiler ablehnen lassen?
class C {} interface I {} struct S {}
Warum oder warum nicht?
Um Ihre Frage direkter nicht zu beantworten: "Warum ist die Welt nicht anders als sie ist?" Fragen sind schwer zu beantworten. Anstatt diese unmögliche Frage zu beantworten, beantworte ich die Frage: "Angenommen, beim Erstellen leerer Aufzählungen wurde dem Designteam ein Fehler gemeldet. Wie hätten Sie auf diese Frage reagiert?" Diese Frage ist immer noch kontrafaktisch, aber zumindest kann ich sie beantworten.
Es stellt sich dann die Frage, ob die Kosten des Features durch seine Vorteile gerechtfertigt sind .
Um zu funktionieren, muss ein Sprachfeature gedacht, entworfen, spezifiziert, implementiert, getestet, dokumentiert und an Kunden versendet werden. Dies ist eine Funktion "Fehler erzeugen", daher muss die Fehlermeldung geschrieben und in einige Dutzend Sprachen übersetzt werden, ebenso wie die Dokumentation. Die fünf Minuten, die ich für die Implementierung der Funktion benötigt hätte, bedeuten für viele Leute, die ziemlich viel bezahlt werden, viele Arbeitsstunden.
Dies sind jedoch nicht die relevanten Kosten. Die Opportunitätskosten sind die relevanten Kosten. Die Budgets sind begrenzt, Funktionen sind nicht kostenlos, und daher bedeutet jede implementierte Funktion, dass eine andere Funktion gekürzt werden muss. Welches Feature von C # möchten Sie geschnitten haben, um dieses Feature zu erhalten? Der verlorene Nutzen aus nicht in der Lage , eine bessere Funktion zu tun ist , die Opportunitätskosten .
Ich stelle auch fest, dass Ihre vorgeschlagene Funktion für niemanden einen offensichtlichen Nutzen hat , was es für das Designkomitee schwierig machen würde, sie zu verkaufen. Vielleicht gibt es einen überzeugenden Vorteil, den ich nicht sehe; wenn ja, was ist das?
Niemand fällt mir ein. "Programme ablehnen, die offensichtlich nicht nützlich sind" ist kein Entwurfsziel von C #.
quelle
Sie können jeden Wert des zugrunde liegenden Integer-Typs (glaube ich
int
standardmäßig) in die Aufzählung umwandeln - also wird er(MyConfusingEnum)42
jetzt von diesem Aufzählungstyp sein.Ich denke nicht, dass es im Allgemeinen eine gute Idee ist, aber es könnte Fälle geben, in denen "Enum" -Werte von einer externen Quelle stammen und Code einfach besser aussieht
enum
.Beispiel (unter der Annahme, dass Code einen "int-basierten Status" in Enum enthält:
enum ExternalDeviceState {}; ExternalDeviceState GetState(){ ... return (ExternalDeviceState )intState;} bool IsDeviceStillOk(ExternalDeviceState currentState) { .... }
Die Spezifikation erlaubt in der Tat eine leere Aufzählung:
enum-declaration: attributesopt enum-modifiersopt enum identifier enum-base(opt) enum-body ;(opt) enum-base: : integral-type enum-body: { enum-member-declarations(opt) } { enum-member-declarations , }
Beachten Sie, dass dies
enum-member-declarations(opt)
explizit als Variante markiert ist, in der sich nichts befindet{}
.quelle
Activator.CreateInstance<MyConfusingEnum>();
ist das gleiche wienew MyConfusingEnum()
. ( docs )Wenn Sie den Konstruktor einer Aufzählung aufrufen, erhalten Sie einen
0
Wert.Aufgrund einer Entwurfsentscheidung kann eine Aufzählung einen beliebigen Wert haben, der für den Hintergrundtyp (normalerweise
int
) gültig ist. Es muss sich nicht um einen in der Aufzählung definierten Wert handeln.Aufgrund dieser Entwurfsentscheidung kann ich Sie auf diese Antwort mit der Frage "Warum löst das Casting von int in einen ungültigen Aufzählungswert KEINE Ausnahme aus?" Verweisen.
@AlexeiLevenkov hat die Spezifikation bereitgestellt, die eine leere Aufzählung zulässt. Wir können davon ausgehen, dass eine leere Aufzählung zulässig ist, da jeder Wert für den Hintergrundtyp gültig ist.
quelle
Wie bereits erwähnt, können Sie dieser Aufzählung jeden Wert zuweisen, den der zugrunde liegende Typ durch eine einfache Umwandlung zulässt. Auf diese Weise können Sie die Typprüfung in Situationen erzwingen, in denen ein int die Sache verwirren würde. Zum Beispiel:
public enum Argb : int {} public void SetColor(Argb a) { ....
oder Sie möchten einige Erweiterungsmethoden haben, ohne den int-Datentyp damit zu überladen
public static Color GetColor(this Argb value) { return new Color( (int)value ); } public static void Deconstruct( this Argb color, out byte alpha, out byte red, out byte green, out byte blue ) { alpha = (byte)( (uint)color >> 24 ); red = (byte)( (uint)color >> 16 ); green = (byte)( (uint)color >> 8 ); blue = (byte)color; }
und benutze es als
var (alpha, red, green, blue) = color;
quelle
public enum Argb : int { NOTHING_TO_SEE_HERE }
funktioniert gut.Ich habe einen in der Java-Welt erlebt.
Es gibt jedoch eine Zeit vor der Kompilierungszeit, in der Sie möglicherweise (noch) nicht alle Werte kennen oder in der Sie einfach noch keine Werte implementieren möchten.
Während des Entwurfs einer API habe ich eine Aufzählung ohne Werte implementiert, um die Referenzierung von anderen Schnittstellen aus zu ermöglichen. Ich habe später Werte hinzugefügt.
quelle
enum foo { NOTHING_DEFINED_HERE_YET }
funktioniert gut.