Ich baue eine Funktion auf, um das Enum.Parse
Konzept zu erweitern
- Ermöglicht das Analysieren eines Standardwerts, falls kein Enum-Wert gefunden wird
- Ist unabhängig von Groß- und Kleinschreibung
Also schrieb ich folgendes:
public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
Ich erhalte eine Fehlerbeschränkung kann keine Sonderklasse sein System.Enum
.
Fair genug, aber gibt es eine Problemumgehung, um eine generische Aufzählung zuzulassen, oder muss ich die Parse
Funktion nachahmen und einen Typ als Attribut übergeben, wodurch die hässliche Boxanforderung an Ihren Code erzwungen wird.
BEARBEITEN Alle Vorschläge unten wurden sehr geschätzt, danke.
Ich habe mich entschieden (ich habe die Schleife verlassen, um die Groß- und Kleinschreibung nicht zu berücksichtigen - ich verwende dies beim Parsen von XML).
public static class EnumUtils
{
public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
if (string.IsNullOrEmpty(value)) return defaultValue;
foreach (T item in Enum.GetValues(typeof(T)))
{
if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
}
return defaultValue;
}
}
BEARBEITEN: (16. Februar 2015) Julien Lebosquain hat kürzlich eine vom Compiler erzwungene typsichere generische Lösung in MSIL oder F # veröffentlicht , die einen Blick wert ist, und eine positive Bewertung. Ich werde diese Bearbeitung entfernen, wenn die Lösung weiter oben auf der Seite sprudelt.
quelle
Antworten:
Da
Enum
Type dieIConvertible
Schnittstelle implementiert, sollte eine bessere Implementierung ungefähr so aussehen:Dies ermöglicht weiterhin die Übergabe von Wertetypen, die implementiert werden
IConvertible
. Die Chancen sind jedoch selten.quelle
Diese Funktion wird endlich in C # 7.3 unterstützt!
Das folgende Snippet (aus den Dotnet-Beispielen ) zeigt, wie:
Stellen Sie sicher, dass Sie Ihre Sprachversion in Ihrem C # -Projekt auf Version 7.3 einstellen.
Originalantwort unten:
Ich bin zu spät zum Spiel, aber ich habe es als Herausforderung angesehen, zu sehen, wie es gemacht werden kann. Dies ist in C # (oder VB.NET nicht möglich, aber für F # nach unten scrollen), in MSIL jedoch möglich . Ich habe dieses kleine ... Ding geschrieben
Was eine Funktion erzeugt, die so aussehen würde , wenn sie gültig wäre C #:
Dann mit folgendem C # -Code:
Leider bedeutet dies, dass dieser Teil Ihres Codes in MSIL anstelle von C # geschrieben wird. Der einzige zusätzliche Vorteil besteht darin, dass Sie diese Methode einschränken können
System.Enum
. Es ist auch eine Art Mist, weil es in einer separaten Assembly zusammengestellt wird. Dies bedeutet jedoch nicht, dass Sie es auf diese Weise bereitstellen müssen.Durch Entfernen der Zeile
.assembly MyThing{}
und Aufrufen von ilasm wie folgt:Sie erhalten ein Netzmodul anstelle einer Baugruppe.
Leider unterstützt VS2010 (und natürlich früher) das Hinzufügen von Netzmodulreferenzen nicht, was bedeutet, dass Sie es beim Debuggen in zwei separaten Assemblys belassen müssen. Die einzige Möglichkeit, sie als Teil Ihrer Assembly hinzuzufügen, besteht darin, csc.exe selbst mit dem
/addmodule:{files}
Befehlszeilenargument auszuführen . Es wäre nicht auch soIn einem MSBuild-Skript schmerzhaft. Wenn Sie mutig oder dumm sind, können Sie csc natürlich jedes Mal manuell ausführen. Und es wird sicherlich komplizierter, da mehrere Baugruppen Zugriff darauf benötigen.So kann es in .Net gemacht werden. Lohnt sich der zusätzliche Aufwand? Ähm, ich denke, ich werde dich darüber entscheiden lassen.
F # Lösung als Alternative
Zusätzliches Guthaben: Es stellt sich heraus, dass eine generische Einschränkung
enum
in mindestens einer anderen .NET-Sprache neben MSIL: F # möglich ist.Diese Sprache ist einfacher zu warten, da es sich um eine bekannte Sprache mit vollständiger Unterstützung für Visual Studio IDE handelt. Sie benötigen jedoch noch ein separates Projekt in Ihrer Lösung. Es erzeugt jedoch natürlich erheblich unterschiedliche IL (der Code ist sehr unterschiedlich) und es stützt sich auf die
FSharp.Core
Bibliothek, die wie jede andere externe Bibliothek Teil Ihrer Distribution werden muss.So können Sie es verwenden (im Grunde das gleiche wie die MSIL-Lösung) und zeigen, dass es bei ansonsten synonymen Strukturen korrekt fehlschlägt:
quelle
There's no particularly unusual reason why not; we have lots of other things to do, limited budgets, and this one has never made it past the "wouldn't this be nice?" discussion in the language design team.
T
,System.Enum
nicht in der Lage ist, alle Dinge damit zu tunT
die Autoren von C # dachten, sie könnten es genauso gut insgesamt verbieten die Leute erwarten könnten. Ich halte die Entscheidung für unglücklich, da C # jede spezielle Behandlung vonSystem.Enum
Einschränkungen einfach ignoriert hatte , wäre es möglich gewesen, eineHasAnyFlags<T>(this T it, T other)
Erweiterungsmethode zu schreiben , die um Größenordnungen schneller war alsEnum.HasFlag(Enum)
und die ihre Argumente typgeprüft hat.C # ≥ 7.3
Ab C # 7.3 (verfügbar mit Visual Studio 2017 ≥ v15.7) ist dieser Code jetzt vollständig gültig:
C # ≤ 7,2
Sie können eine echte Compiler-Enum-Einschränkung erzwingen lassen, indem Sie die Vererbung von Einschränkungen missbrauchen. Der folgende Code gibt gleichzeitig eine
class
und einestruct
Einschränkung an:Verwendungszweck:
Hinweis: Dies ist in der C # 5.0-Sprachspezifikation ausdrücklich angegeben:
quelle
EnumClassUtils<System.Enum>
reicht aus, um T aufSystem.Enum
alle abgeleiteten Typen zu beschränken.struct
onParse
beschränkt es dann weiter auf einen echten Aufzählungstyp. Sie müssen sichEnum
irgendwann darauf beschränken. Dazu muss Ihre Klasse verschachtelt sein. Siehe gist.github.com/MrJul/7da12f5f2d6c69f03d79where TClass : class
Einschränkung hier?enum DefinitelyNotAnInt : byte { Realize, That, I, Am, Not, An, Int }
enum AlsoNotAnInt : long { Well, Bummer }
Bearbeiten
Die Frage wurde jetzt von Julien Lebosquain hervorragend beantwortet . Ich mag auch seine Antwort mit erweitern
ignoreCase
,defaultValue
und optionalen Argumenten, während des HinzufügenTryParse
undParseOrDefault
.Anwendungsbeispiele:
Alt
Meine alten Verbesserungen an Viveks Antwort unter Verwendung der Kommentare und "neuen" Entwicklungen:
TEnum
für Klarheit für BenutzerTryParse
handhaben lassenignoreCase
mit dem vorhandenen Parameter (eingeführt in VS2010 / .Net 4)default
Wert (eingeführt in VS2005 / .Net 2).defaultValue
undignoreCase
ergebend:
quelle
Sie können einen statischen Konstruktor für die Klasse definieren, der überprüft, ob der Typ T eine Aufzählung ist, und eine Ausnahme auslösen, wenn dies nicht der Fall ist. Dies ist die Methode, die Jeffery Richter in seinem Buch CLR via C # erwähnt hat.
Dann können Sie in der Analysemethode einfach Enum.Parse (typeof (T), input, true) verwenden, um von string in enum zu konvertieren. Der letzte wahre Parameter dient zum Ignorieren der Eingabe.
quelle
Enum
T
bei der Ausführung des Konstruktors ein Non angegeben haben . Dies ist jedoch viel schöner, als auf einen Instanzkonstruktor zu warten.Es sollte auch berücksichtigt werden, dass die Veröffentlichung von C # 7.3 unter Verwendung von Enum-Einschränkungen sofort unterstützt wird, ohne dass zusätzliche Überprüfungen und andere Vorgänge erforderlich sind.
Wenn Sie also die Sprachversion Ihres Projekts in C # 7.3 geändert haben, funktioniert der folgende Code einwandfrei:
Falls Sie nicht wissen, wie Sie die Sprachversion in C # 7.3 ändern können, sehen Sie den folgenden Screenshot:
BEARBEITEN 1 - Erforderliche Visual Studio-Version und unter Berücksichtigung von ReSharper
Damit Visual Studio die neue Syntax erkennt, benötigen Sie mindestens Version 15.7. Sie finden dies auch in den Versionshinweisen von Microsoft, siehe Versionshinweise zu Visual Studio 2017 15.7 . Vielen Dank an @MohamedElshawaf für den Hinweis auf diese gültige Frage.
Bitte beachten Sie auch, dass in meinem Fall ReSharper 2018.1 zum Zeitpunkt des Schreibens dieser EDIT C # 7.3 noch nicht unterstützt. Wenn ReSharper aktiviert ist, wird die Enum-Einschränkung als Fehler hervorgehoben, der besagt, dass 'System.Array', 'System.Delegate', 'System.Enum', 'System.ValueType', 'object' nicht als Typparameter-Einschränkung verwendet werden können . ReSharper schlägt als schnelle Lösung vor, die 'Enum'-Einschränkung des Typparameters T der Methode zu entfernen
Wenn Sie ReSharper jedoch vorübergehend unter Extras -> Optionen -> ReSharper Ultimate -> Allgemein deaktivieren Sie feststellen, dass die Syntax vollkommen in Ordnung ist, wenn Sie VS 15.7 oder höher und C # 7.3 oder höher verwenden.
quelle
where T : struct, Enum
, um zu vermeiden, sichSystem.Enum
selbst als Typparameter zu übergeben.struct, Enum
. Meine Begründung wird in der Antwort und den Kommentaren hier erklärt .Ich habe die Probe von Dimarzionist modifiziert. Diese Version funktioniert nur mit Enums und lässt keine Strukturen durch.
quelle
Ich habe versucht, den Code ein wenig zu verbessern:
quelle
defaultValue.ToString("D", System.Globalization.NumberFormatInfo.CurrentInfo)
können, obwohl Sie nicht wissen, um welche Art von Aufzählung es sich handelt, sondern nur, dass das Objekt eine Aufzählung ist.IsDefined
wird jedoch die Unempfindlichkeit gegenüber Groß- und Kleinschreibung zerstören. Im GegensatzParse
,IsDefined
hat keinignoreCase
Argument, und MSDN sagt , dass es nur exakten Fall paßt .Ich habe spezielle Anforderungen, bei denen ich Enum mit Text verwenden muss, der mit dem Enum-Wert verknüpft ist. Wenn ich beispielsweise enum verwende, um den Fehlertyp anzugeben, müssen Fehlerdetails beschrieben werden.
quelle
Hoffe das ist hilfreich:
quelle
return (TValue)Enum.Parse(typeof (TValue), value);
durchreturn (TValue)Enum.Parse(typeof (TValue), value, true);
Interessanterweise ist dies anscheinend in anderen Sprachen möglich (Managed C ++, IL direkt).
Zitieren:
Wer weiß
quelle
Das ist meine Einstellung dazu. Kombiniert aus den Antworten und MSDN
MSDN-Quelle
quelle
TEnum
tatsächlich um einen Aufzählungstyp handelt, es sich jedochtext
um eine leere Zeichenfolge handelt, wird dasArgumentException
Sprichwort "TEnum muss ein Aufzählungstyp sein" angezeigt, obwohl dies der Fall ist.Die vorhandenen Antworten gelten ab C # <= 7.2. Es gibt jedoch eine C # -Sprachen- Funktionsanforderung (die an eine CoreFX- Funktionsanforderung gebunden ist ), um Folgendes zu ermöglichen:
Zum Zeitpunkt des Schreibens ist das Feature "In Diskussion" bei den Sprachentwicklungstreffen.
BEARBEITEN
Wie pro Nawfal ‚s Info, wird dies wird in C # eingeführt 7.3 .
quelle
Das hat mir immer gefallen (Sie können es nach Bedarf ändern):
quelle
Ich mochte Christopher Currens 'Lösung mit IL, aber für diejenigen, die sich nicht mit kniffligen Geschäften befassen wollen, MSIL in ihren Erstellungsprozess einzubeziehen, habe ich eine ähnliche Funktion in C # geschrieben.
Bitte beachten Sie jedoch, dass Sie keine generischen Einschränkungen wie verwenden können
where T : Enum
da Enum ein spezieller Typ ist. Daher muss ich überprüfen, ob der angegebene generische Typ wirklich enum ist.Meine Funktion ist:
quelle
Ich habe die Lösung von Vivek in eine Dienstprogrammklasse eingekapselt, die Sie wiederverwenden können. Bitte beachten Sie, dass Sie für Ihren Typ weiterhin Typeinschränkungen "wobei T: struct, IConvertible" definieren sollten.
quelle
Ich habe eine Erweiterungsmethode erstellt.
to get integer value from enum
Schauen Sie sich die Methodenimplementierung anDies ist die Verwendung
quelle
Wie in anderen Antworten zuvor angegeben; Dies kann zwar nicht im Quellcode ausgedrückt werden, kann jedoch tatsächlich auf IL-Ebene erfolgen. Die Antwort von @Christopher Currens zeigt, wie die IL das macht.
Mit Fody 's Add-In ExtraConstraints.Fody gibt es eine sehr einfache Möglichkeit, dies mit Build-Tools zu erreichen. Fügen Sie einfach ihre Nuget-Pakete (
Fody
,ExtraConstraints.Fody
) zu Ihrem Projekt hinzu und fügen Sie die Einschränkungen wie folgt hinzu (Auszug aus der Readme-Datei von ExtraConstraints):und Fody fügt die erforderliche IL hinzu, damit die Einschränkung vorhanden ist. Beachten Sie auch die zusätzliche Funktion zum Einschränken von Delegaten:
In Bezug auf Enums sollten Sie auch das hochinteressante Enums.NET beachten .
quelle
Dies ist meine Implementierung. Grundsätzlich können Sie jedes Attribut einrichten und es funktioniert.
quelle
Wenn es in Ordnung ist, später direktes Casting zu verwenden, können Sie die
System.Enum
Basisklasse in Ihrer Methode verwenden, wo immer dies erforderlich ist. Sie müssen nur die Typparameter sorgfältig ersetzen. Die Implementierung der Methode wäre also wie folgt:Dann können Sie es wie folgt verwenden:
quelle
Enum.ToObject()
würde zu einem flexibleren Ergebnis führen. Hinzu kommt, dass Sie die Zeichenfolgenvergleiche ohne Groß- und Kleinschreibung durchführen können, wodurch die Notwendigkeit eines AufrufsToLower()
Der Vollständigkeit halber ist das Folgende eine Java-Lösung. Ich bin sicher, dass dies auch in C # möglich ist. Es wird vermieden, dass der Typ irgendwo im Code angegeben werden muss. Stattdessen geben Sie ihn in den Zeichenfolgen an, die Sie analysieren möchten.
Das Problem ist, dass es keine Möglichkeit gibt zu wissen, mit welcher Aufzählung der String übereinstimmen könnte. Die Antwort besteht also darin, dieses Problem zu lösen.
Akzeptieren Sie einen String, der sowohl die Aufzählung als auch den Wert in der Form "enumeration.value" enthält, anstatt nur den Zeichenfolgenwert zu akzeptieren. Arbeitscode ist unten - erfordert Java 1.8 oder höher. Dies würde auch das XML präziser machen, da Sie so etwas wie color = "Color.red" anstelle von color = "red" sehen würden.
Sie würden die Methode acceptEnumeratedValue () mit einer Zeichenfolge aufrufen, die den Namen des Aufzählungsnamens und des Wertes enthält.
Die Methode gibt den formalen Aufzählungswert zurück.
quelle