Wie konvertiere ich von System.Enum in eine Basis-Ganzzahl?

93

Ich möchte eine generische Methode zum Konvertieren eines von System.Enum abgeleiteten Typs in seinen entsprechenden ganzzahligen Wert erstellen, ohne umzuwandeln und vorzugsweise ohne eine Zeichenfolge zu analysieren.

ZB was ich will ist so etwas:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

Dies scheint jedoch nicht zu funktionieren. Resharper meldet, dass Sie den Ausdruck vom Typ 'System.Enum' nicht in den Typ 'int' umwandeln können.

Jetzt habe ich diese Lösung gefunden, aber ich hätte lieber etwas effizienteres.

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

Irgendwelche Vorschläge?

orj
quelle
1
Ich glaube, es ist der Compiler, der sich beschwert, nicht Resharper.
Kugel
1
Nicht unbedingt. Ich habe eine Erweiterungsmethode für System.Enum, und gelegentlich beschließt Resharper, sich zu beschweren: Der Instanzargumenttyp 'Some.Cool.Type.That.Is.An.Enum' kann nicht in 'System.Enum' konvertiert werden, wenn es sich zweifellos um eine Aufzählung handelt. Wenn ich den Code kompiliere und ausführe, funktioniert er einwandfrei. Wenn ich dann VS herunterfahre, den Resharper-Cache wegblase und ihn wieder hochfahre, ist alles in Ordnung, sobald das erneute Scannen abgeschlossen ist. Für mich ist es eine Art Cache-Snafu. Könnte für ihn dasselbe sein.
Mir
@Mir Ich habe ReSharper auch darüber "beschweren" lassen. Gleiche Lösung für mich. Ich bin mir nicht sicher, warum diese Typen verwechselt werden, aber es ist definitiv nicht der Compiler.
Akousmata

Antworten:

134

Wenn du nicht besetzen willst,

Convert.ToInt32()

könnte den Trick machen.

Die direkte Besetzung (via (int)enumValue) ist nicht möglich. Beachten Sie, dass dies auch „gefährlich“ wäre da eine Enumeration verschiedene zugrunde liegende Typen haben kann ( int, long, byte...).

Formaler: System.EnumHat keine direkte Vererbungsbeziehung mit Int32(obwohl beide ValueTypes sind), so dass die explizite Umwandlung innerhalb des Typsystems nicht korrekt sein kann

MartinStettner
quelle
2
Converter.ToInteger(MyEnum.MyEnumConstant);wird Ihnen hier keinen Fehler geben. Bitte bearbeiten Sie diesen Teil.
Nawfal
Danke, @nawfal, du hast recht. Ich habe die Antwort bearbeitet (und etwas Neues gelernt :) ...)
MartinStettner
Funktioniert nicht, wenn der zugrunde liegende Typ vonint
Alex Zhukovskiy
@YaroslavSivakov es wird nicht funktionieren, zB für longenum.
Alex Zhukovskiy
System.Enumist eine Klasse, also ein Referenztyp. Die Werte sind wahrscheinlich eingerahmt.
Teejay
42

Ich habe es zum Laufen gebracht, indem ich es auf ein Objekt und dann auf ein int geworfen habe:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

Das ist hässlich und wahrscheinlich nicht der beste Weg. Ich werde weiter damit herumspielen, um zu sehen, ob ich mir etwas Besseres einfallen lassen kann ...

EDIT: Ich wollte gerade posten, dass Convert.ToInt32 (enumValue) auch funktioniert, und bemerkte, dass MartinStettner mich geschlagen hat.

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

Prüfung:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

EDIT 2: In den Kommentaren sagte jemand, dass dies nur in C # 3.0 funktioniert. Ich habe das gerade in VS2005 so getestet und es hat funktioniert:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }
BKostenlos
quelle
Ich habe dir +1 gegeben, aber diese Lösung wird nur mit C # 3.0 und höher funktionieren.
Vadim
Ich denke, das befriedigt nur den Compiler. Haben Sie es geschafft, dies zur Laufzeit zu testen? Ich konnte dieser Funktion keinen Wert übergeben ...
MartinStettner
Weil es eine Erweiterungsmethode ist? Oder ist etwas anderes an Aufzählungen in älteren Versionen von C #?
BFree
Ich habe am Ende meines Beitrags einen Test hinzugefügt. Ich habe das versucht und es hat bei mir funktioniert.
BFree
@BFree: Es scheint nur mit Erweiterungsmethoden zu funktionieren. Ich habe es mit einer "alten" Funktion (in C # 2.0) versucht und es nicht geschafft, die Syntax zu finden, um tatsächlich einen Wert an sie zu übergeben (darum ging es in meinem vorherigen Kommentar)
MartinStettner
14

Wenn Sie eine Aufzählung in den zugrunde liegenden Typ konvertieren müssen (nicht alle Aufzählungen werden von unterstützt int), können Sie Folgendes verwenden:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));
Drew Noakes
quelle
5
Dies ist die einzige kugelsichere Antwort.
Rudey
Ist dies Ursache Boxen Unboxing?
b.ben
@ b.ben ja. Convert.ChangeTypeakzeptiert objectfür das erste Argument und gibt zurück object.
Drew Noakes
Ja, da stimme ich @Rudey tatsächlich zu, trotzdem sollten Sie Leistungstests durchführen. Die Lösung von BFree ist bei weitem die schnellste. Etwa achtmal schneller. Jedenfalls reden wir immer noch über Zecken.
Zwanzig
10

Warum müssen Sie das Rad mit einer Hilfsmethode neu erfinden? Es ist völlig legal, einen enumWert auf den zugrunde liegenden Typ zu setzen.

Es ist weniger tippen und meiner Meinung nach besser lesbar, ...

int x = (int)DayOfWeek.Tuesday;

... anstatt so etwas wie ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();
LukeH
quelle
6
Ich möchte JEDEN Aufzählungswert konvertieren. Das heißt, ich habe eine Vielzahl von Klassen mit Aufzählungsfeldern, die von einem Code generisch verarbeitet werden. Aus diesem Grund ist eine einfache Umwandlung in int nicht angemessen.
Orj
@orj, ich bin mir nicht sicher was du meinst. Können Sie ein Beispiel geben, das die Verwendung zeigt, die Sie benötigen?
LukeH
2
Manchmal kann man eine Aufzählung nicht direkt umwandeln, wie im Fall einer generischen Methode. Da generische Methoden nicht auf Aufzählungen beschränkt werden können, müssen Sie sie auf so etwas wie struct beschränken, das nicht in ein int umgewandelt werden kann. In diesem Fall können Sie Convert.ToInt32 (enum) verwenden, um dies zu tun.
Daniel T.
Ich mag die Idee, dass die Erweiterungsmethode ToInteger () dem gleichen Format wie ToString () folgt. Ich denke, es ist weniger lesbar, einerseits in int umzuwandeln, wenn Sie den int-Wert möchten, und ToString () zu verwenden, wenn wir den Zeichenfolgenwert benötigen, insbesondere wenn der Code nebeneinander steht.
Louise Eggleton
Die Verwendung einer Erweiterungsmethode kann außerdem die Konsistenz Ihrer Codebasis erhöhen. Da es, wie @nawfal betonte, verschiedene Möglichkeiten gibt, den int-Wert aus einer Aufzählung abzurufen, bedeutet das Erstellen einer Erweiterungsmethode und das Erzwingen ihrer Verwendung, dass Sie jedes Mal, wenn Sie den int-Wert einer Aufzählung erhalten, dieselbe Methode verwenden
Louise Eggleton
4

Aus meiner Antwort hier :

Gegeben ewie in:

Enum e = Question.Role;

Dann funktionieren diese:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

Die letzten beiden sind einfach hässlich. Der erste sollte besser lesbar sein, der zweite ist jedoch viel schneller. Oder vielleicht ist eine Erweiterungsmethode das Beste, das Beste aus beiden Welten.

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

Jetzt können Sie anrufen:

e.GetValue<int>(); //or
e.GetIntValue();
nawfal
quelle
Bist du sicher, dass der zweite schneller ist? Ich nehme an, es wird die Aufzählung boxen und dann wieder auf int entpacken?
Jojo
1
@yoyo Die eVariable hat bereits die Box-Instanz des tatsächlichen Werttyps , dh Enum. Wenn Sie schreiben, ist Enum e = Question.Role;es bereits verpackt. Die Frage betrifft die Konvertierung des Boxed System.EnumBacks in den zugrunde liegenden Int-Typ (Unboxing). Hier ist Unboxing also nur das Leistungsproblem. (int)(object)eist ein direkter Unbox-Anruf; Ja sollte schneller sein als andere Ansätze. Sehen Sie dies
Nawfal
Danke für die Erklärung. Ich hatte den falschen Eindruck, dass eine Instanz von System.Enum ein Wert ohne Box ist. Siehe auch dies - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo
@ yoyo hmm ja. Das relevante Zitat aus dem Link: Von jedem Aufzählungstyp zum Typ System.Enum.
Nawfal
1

Das Casting von a System.Enumnach a intfunktioniert gut für mich (es ist auch auf dem MSDN ). Vielleicht ist es ein Resharper-Fehler.

jpoh
quelle
1
Welche Version der Laufzeit verwenden Sie?
JoshBerke
2
Der Link zeigt das Casting von Untertypen System.Enum, nicht von sich System.Enumselbst.
Nawfal
Ein ähnliches Problem war ein Resharper-Cache-Snafu. Durch das Schließen von VS und das Löschen des Resharper-Cache verschwand der Fehler auch nach dem vollständigen erneuten Scannen.
Mir
1

Da Enums auf Byte, Sbyte, Short, Ushort, Int, Uint, Long und Ulong beschränkt sind, können wir einige Annahmen treffen.

Wir können Ausnahmen während der Konvertierung vermeiden, indem wir den größten verfügbaren Container verwenden. Leider ist nicht klar, welcher Container verwendet werden soll, da ulong für negative Zahlen und long für Zahlen zwischen long.MaxValue und ulong.MaxValue explodiert. Wir müssen zwischen diesen Auswahlmöglichkeiten basierend auf dem zugrunde liegenden Typ wechseln.

Natürlich müssen Sie immer noch entscheiden, was zu tun ist, wenn das Ergebnis nicht in ein int passt. Ich denke, Casting ist in Ordnung, aber es gibt noch einige Fallstricke:

  1. Bei Aufzählungen, die auf einem Typ mit einem Feldraum größer als int (long und ulong) basieren, ist es möglich, dass einige Aufzählungen denselben Wert haben.
  2. Wenn Sie eine Zahl größer als int.MaxValue eingeben, wird eine Ausnahme ausgelöst, wenn Sie sich in einem aktivierten Bereich befinden.

Hier ist mein Vorschlag, ich überlasse es dem Leser zu entscheiden, wo diese Funktion verfügbar gemacht werden soll. als Helfer oder Erweiterung.

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}
Eisenpony
quelle
-1

Vergessen Sie nicht, dass der Enum-Typ selbst eine Reihe statischer Hilfsfunktionen enthält. Wenn Sie lediglich eine Instanz der Enumeration in den entsprechenden Integer-Typ konvertieren möchten, ist das Casting wahrscheinlich der effizienteste Weg.

Ich denke, ReSharper beschwert sich, weil Enum keine Aufzählung eines bestimmten Typs ist und die Aufzählungen selbst von einem skalaren Werttyp abgeleitet sind, nicht von Enum. Wenn Sie generisch anpassbares Casting benötigen, würde ich sagen, dass dies gut zu Ihnen passt (beachten Sie, dass der Aufzählungstyp selbst auch im generischen enthalten ist:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

Dies könnte dann folgendermaßen verwendet werden:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

Ich kann es nicht mit Sicherheit sagen, aber der obige Code wird möglicherweise sogar durch die Typinferenz von C # unterstützt, was Folgendes ermöglicht:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);
jrista
quelle
1
Leider kann in Ihrem Beispiel E ein beliebiger Typ sein. Generika erlauben mir nicht, Enum als Typparametereinschränkung zu verwenden. Ich kann nicht sagen: wo T: Enum
Vadim
Sie können jedoch auch T: struct verwenden. Es ist nicht ganz so streng, wie Sie es wollen, aber es würde jeden Referenztyp herausschneiden (was ich denke, was Sie versuchen zu tun.)
jrista
Sie können Folgendes verwenden: if (! typeof (E) .IsEnum) löst eine neue ArgumentException aus ("E muss ein Aufzählungstyp sein");
Diadiora
1
Dies ist nicht einmal im entferntesten korrekt, der statische Helfer ist nicht einmal gültig.
Nicholi
1
Warum sollte jemand EnumHelpers.Convert<int, StopLight>(StopLight.Red);anstelle von verwenden (int)StopLight.Read;? Auch die Frage spricht System.Enum.
Nawfal