.NET: Warum wird Enums Bereich / Wert nicht überprüft?

8

Das hat mich schon immer gestört. Vielleicht kann jemand mit ein wenig hardcore Wissen über .NET Interna es mir erklären.

Angenommen, ich definiere eine Aufzählung wie folgt:

%Vor%

Nun nehme ich auch an, dass ich irgendwo in meinem Code den folgenden Code habe:

%Vor%

Dies wird gut kompilieren, und es wird keine Ausnahme ausgelöst, obwohl der Wert 5 eindeutig kein gültiger Wert ist, der in Foo definiert ist.

Oder überlegen Sie Folgendes:

%Vor%

Auch hier keine Ausnahme.

Meiner Meinung nach sollte dies zu einer Typ-Mismatch-Ausnahme führen, weil 5 kein Foo ist. Aber die Designer haben sich entschieden, das nicht zu tun.

Nun habe ich eine Erweiterungsmethode geschrieben, die bestätigen kann, dass dies der Fall ist, und es ist keine große Sache, sie aufzurufen, um sicherzustellen, dass dies der Fall ist, aber ich muss Reflektion verwenden (mit all ihrer Leistung) Strafen und was nicht).

Also, noch einmal, welcher zwingende Grund könnte da gewesen sein, der die Entscheidung dazu getrieben haben könnte, kein geprüftes Enum zu haben?

Zur Referenz: die MSDN-Dokumentation für die Enum-Klasse :

  

Wenn Sie eine Methode oder Eigenschaft definieren   das erfordert eine aufgezählte Konstante als a   Wert, prüfen Sie den Wert.   Der Grund dafür ist, dass Sie ein   numerischer Wert für den Aufzählungstyp   auch wenn dieser numerische Wert nicht ist   definiert in der Aufzählung.

    
Mike Hofer 11.01.2009, 13:38
quelle

9 Antworten

13

Das Problem war Leistung. Es ist ziemlich einfach, eine eingecheckte Enumeration für normale Enums wie Color

zu haben %Vor%

Das Problem besteht jedoch für Enumerationen, die als Bit-Flags verwendet werden.

%Vor%

Eine bitweise Überprüfung auf jede einzelne ganze Zahl, die in einen Enum-Wert konvertiert wird, wurde als zu teuer erachtet. Daher die entspannte Umwandlung in Enum-Werte.

    
JaredPar 11.01.2009, 15:24
quelle
4

Die Bereichsüberprüfung hat möglicherweise unnötige Kosten. Es ist daher sinnvoll, dies nicht implizit durchzuführen. Wie bereits erwähnt, erfordert [Flags] , dass keine solche Überprüfung stattfindet. Wenn die Laufzeitumgebung auf das Vorhandensein von [Flags] prüfen würde, würde dies jedes Mal, wenn Sie eine Konvertierung durchführen, immer noch zu einem Laufzeitverlust führen.

Dies ist nur möglich, wenn der Compiler das Attribut [Flags] kennt. Ich denke, das wurde nicht getan, um die Menge an Laufzeitwissen zu reduzieren, das in den Compiler fest codiert ist.

    
Konrad Rudolph 11.01.2009 13:49
quelle
2

Wenn sie Bereich überprüft werden, wie hätten Sie [Flags] enums und kombinieren Sie sie bitweise oder?

Ein Beispiel wäre ControlStyles enum

    
Mehrdad Afshari 11.01.2009 13:40
quelle
1

Zwei Gründe kommen mir in den Sinn. Erstens erfordert das Generieren eines Werts außerhalb des Bereichs eine Umwandlung. Wenn Sie absichtlich casten, warum sollten Sie erwarten, zur Laufzeit geschlagen zu werden? Es wäre viel einfacher gewesen, die Besetzung zu verbieten.

Ein anderer unwiderstehlicher ist dieser:

%Vor%     
Hans Passant 11.01.2009 18:56
quelle
1

Ich kann zwei Gründe sehen:

  1. [Flags] funktionieren reibungsloser ohne Überprüfungen. Für Ihr Beispiel

    (Foo) 5 == Foo.Eenie | Foo.Moe;

  2. Enum ist ein Werttyp. Wenn Sie es nicht initialisieren, wird es gleich Null sein. Wenn Sie enum-Werte überprüfen möchten, ist es nicht klar, wann in diesem Fall eine Ausnahme ausgelöst werden soll. Wenn Sie z. B. eine Instanz einer Klasse erstellen, die diese enum als Feld enthält, kann sich ein Wert Null ergeben.

    Das aktuelle Verhalten ist also konsistenter - Sie wissen nur, dass Sie Werte außerhalb des zulässigen Bereichs haben und diese überprüfen können.

Darüber hinaus sollten Sie Ihre Prüfungen immer explizit ausführen und Ausnahmen für Werte senden, die Sie nicht verarbeiten können. Andernfalls kann das Hinzufügen neuer Werte zu Ihrer Enumeration das Verhalten Ihres vorhandenen Codes ändern. Glücklicherweise verwenden Sie eine einzelne switch-Anweisung ist eine Methode, die Wert zurückgibt, Compiler wird Sie explizit angeben, was Sie tun möchten, wenn keine Übereinstimmungen gefunden werden - im Standardbereich des Schalters nach, müssen Sie einen Wert zurückgeben oder eine Ausnahme auslösen . NotSupportedException von bevorzugten in den meisten Fällen.

    
Konstantin Spirin 11.01.2009 20:29
quelle
0

Dies liegt daran, dass (Foo)5 ist Foo.Eenie | Foo.Moe

    
Pablo Retyk 11.01.2009 13:46
quelle
0

Ich würde sagen, der Grund liegt darin, dass die Enums nur vom Compiler zur Kompilierzeit typgeprüft und eingepflegt werden. Besonders nützlich, um enums mit dem Attribut Flags zu erweitern (da es plötzlich vorwärtskompatibel wird ...

)     
Rowland Shaw 11.01.2009 14:25
quelle
0

Microsoft C # -Programmierhandbuch gibt ausdrücklich vor, nicht zu tun, wonach Sie fragen:

  

Es ist möglich, dem meetingDay einen beliebigen ganzzahligen Wert zuzuweisen. Diese Codezeile erzeugt beispielsweise keinen Fehler: meetingDay = (Days) 42. Dies sollten Sie jedoch nicht tun, da die implizite Erwartung darin besteht, dass eine enum-Variable nur einen der durch die enum definierten Werte enthält. Um einer Variablen eines Aufzählungstyps einen beliebigen Wert zuzuweisen, wird ein hohes Risiko für Fehler eingeführt.

int ist wirklich nur der Speichertyp. Tatsächlich ist es möglich, andere Integer-Speichertypen anzugeben , wie zB byte:

%Vor%

Der Speichertyp bestimmt, wie viel Speicher eine Aufzählung verwendet.

    
JeffH 10.04.2009 20:17
quelle
-1

Tut mir leid, Necro. Ist es möglich, dass Sie hier ein Enum mit einer Schlüsselwert-Sammlung verwechseln?

Wenn Sie einem Enum-Item einen Integer Meenie = 2 zuweisen, sagen Sie nur, dass Meenie der 3. Index ist, und alles danach, wenn nicht angegeben, wird einen Index von 2+ nehmen (Entfernung von Meenie) ). Wenn Sie also nach Foo [5] suchen, suchen Sie nach Index 5, nicht nach einem Schlüssel mit 5 als Wert.

In den meisten Fällen würden Sie das sowieso nicht tun; Du würdest nach Foo.Meenie fragen - das ist der Punkt der Enumeration, um einen bekannten Bereich von Werten festzulegen und dann auf sie anhand ihrer exponierten Namen zu verweisen. Es ist nur eine Entwicklerfreundlichkeit. Es gibt bessere Strukturen für das, was Sie in Ihrem Beispiel tun.

    
user164226 16.12.2009 15:42
quelle

Tags und Links