Konvertieren Sie eine Aufzählung in eine andere Art von Aufzählung


119

Ich habe eine Aufzählung von zum Beispiel ' Gender' ( Male =0 , Female =1) und ich habe eine andere Aufzählung von einem Dienst, der eine eigene Gender-Aufzählung hat ( Male =0 , Female =1, Unknown =2)

Meine Frage ist, wie kann ich etwas schnelles und schönes schreiben, um es von ihrer Aufzählung in meine umzuwandeln?


5
In was möchten Sie "unbekannt" konvertieren?
Pavel Minaev

Sie können die Aufzählung in andere Aufzählungstypen typisieren,
Gowtham S

Antworten:


87

Die Verwendung einer Erweiterungsmethode funktioniert recht gut, wenn Sie die beiden von Nate vorgeschlagenen Konvertierungsmethoden verwenden:

public static class TheirGenderExtensions
{
    public static MyGender ToMyGender(this TheirGender value)
    {
        // insert switch statement here
    }
}

public static class MyGenderExtensions
{
    public static TheirGender ToTheirGender(this MyGender value)
    {
        // insert switch statement here
    }
}

Offensichtlich müssen Sie keine separaten Klassen verwenden, wenn Sie dies nicht möchten. Ich bevorzuge es, Erweiterungsmethoden nach den Klassen / Strukturen / Aufzählungen zu gruppieren, für die sie gelten.


233

Gegeben Enum1 value = ..., wenn Sie mit Namen meinen:

Enum2 value2 = (Enum2) Enum.Parse(typeof(Enum2), value.ToString());

Wenn Sie mit numerischem Wert meinen, können Sie normalerweise nur Folgendes umwandeln:

Enum2 value2 = (Enum2)value;

(Mit der Besetzung möchten Sie möglicherweise nach Enum.IsDefinedgültigen Werten suchen.)


16
Dies ist die bessere Antwort
Nicholas

1
Hier ist eine Version, die Folgendes verwendet Enum.Tryparse: Auf Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; diese Weise können Sie Eingabewerte verarbeiten, die nicht vorhanden sind, Enum2ohne dass Sie aufgerufene s aufrufen Enum.IsDefinedoder abfangen müssen . Beachten Sie, dass die Reihenfolge der Parameter mehr oder weniger umgekehrt ist . ArgumentExceptionEnum.ParseEnum.Parse
Sander

47

Wandeln Sie einfach eine in int und dann in die andere Aufzählung um (vorausgesetzt, Sie möchten, dass die Zuordnung basierend auf dem Wert erfolgt):

Gender2 gender2 = (Gender2)((int)gender1);

3
Obwohl es unwahrscheinlich ist, dass es "in freier Wildbahn" gesehen wird, und es sehr unwahrscheinlich ist, dass dies bei den Geschlechtern der Fall ist, könnte es eine Aufzählung geben, die durch ein long(oder ulong) und nicht durch ein Element unterstützt wird, intdessen Mitglieder über int.MaxValue(oder unter) definiert sind int.MinValue), in diesem Fall könnte die Umwandlung in intüberlaufen und Sie würden einen undefinierten Aufzählungswert erhalten, der definiert werden sollte.
Rich O'Kelly

natürlich. Der richtige Weg wäre (Gender2) ((zugrunde liegenden Typ hier einfügen) gender1), aber ich denke, das obige Beispiel gibt die richtige Idee, damit ich es nicht ändere.
Adrian Zanescu

3
Dies erfordert, dass die beiden Aufzählungen dieselben Werte in derselben Reihenfolge haben. Obwohl es dieses spezielle Problem löst, ist es wirklich spröde und ich würde es im Allgemeinen nicht für die Enum-Zuordnung verwenden.
Sonicblis

2
na ja .... duh! . Das Mapping muss auf etwas basieren. In diesem Fall befindet sich die Zuordnung auf einem ganzzahligen Wert. Für die Zuordnung basierend auf dem Namen benötigen Sie einen anderen Code. Für eine andere Art der Zuordnung etwas anderes. Niemand sagte, dies sei "für Enum-Mapping im Allgemeinen", und dieser Fall existiert nur, wenn Sie versuchen können, anzugeben, was "Mapping im Allgemeinen" bedeutet
Adrian Zanescu,

20

Um genau zu sein, erstelle ich normalerweise ein Funktionspaar, eine, die Enum 1 verwendet und Enum 2 zurückgibt, und eine, die Enum 2 verwendet und Enum 1 zurückgibt. Jede besteht aus einer case-Anweisung, die Eingaben den Ausgaben zuordnet, und der Standardfall löst eine Ausnahme mit a aus Nachricht, die sich über einen unerwarteten Wert beschwert.

In diesem speziellen Fall könnten Sie die Tatsache ausnutzen, dass die ganzzahligen Werte von männlich und weiblich gleich sind, aber ich würde dies vermeiden, da es hackisch ist und zu Brüchen führen kann, wenn sich eine der Aufzählungen in Zukunft ändert.


7
+1 Ich habe viele Entwickler gesehen, die den Drang aufgegeben haben, einen ganzzahligen Wert von Aufzählungen zu verwenden, um sie zu konvertieren, aber dies ist sehr fehleranfällig. Die Methode der alten Schule, 2 Funktionen zu schreiben, hat sich im Laufe der Zeit bewährt ...
Hemant

20

Wenn wir haben:

enum Gender
{
    M = 0,
    F = 1,
    U = 2
}

und

enum Gender2
{
    Male = 0,
    Female = 1,
    Unknown = 2
}

Wir können sicher tun

var gender = Gender.M;
var gender2   = (Gender2)(int)gender;

Oder auch

var enumOfGender2Type = (Gender2)0;

Wenn Sie den Fall behandeln möchten, in dem eine Aufzählung auf der rechten Seite des '=' - Zeichens mehr Werte enthält als die Aufzählung auf der linken Seite, müssen Sie Ihre eigene Methode / Ihr eigenes Wörterbuch schreiben, um dies zu behandeln, wie von anderen vorgeschlagen.


Ihre Antwort ist wie eine Frage zu stellen!? Wenn ja, ist dies keine Antwort und wenn nein, gibt es oben eine ähnliche Antwort ;).
shA.t

13

Sie könnten eine einfache generische Erweiterungsmethode wie diese schreiben

public static T ConvertTo<T>(this object value)            
    where T : struct,IConvertible
{
    var sourceType = value.GetType();
    if (!sourceType.IsEnum)
        throw new ArgumentException("Source type is not enum");
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Destination type is not enum");
    return (T)Enum.Parse(typeof(T), value.ToString());
}

1
Der Fall fehlender Werte, wie in den obigen Antworten vorgeschlagen, wird nicht behandelt. Sie sollten diese Erweiterungsmethode auch für diesen Fall ändern.
eRaisedToX

8

Sie könnten eine einfache Funktion wie die folgende schreiben:

public static MyGender ConvertTo(TheirGender theirGender)
{
    switch(theirGender)
    {
        case TheirGender.Male:
            break;//return male
        case TheirGender.Female:
            break;//return female
        case TheirGender.Unknown:
            break;//return whatever
    }
}

1
Das ist keine Funktion. erwartet 'MyGender' und du bist zurück 'void'
bl4ckr0se

7

Hier ist eine Version der Erweiterungsmethode, wenn jemand interessiert ist

public static TEnum ConvertEnum<TEnum >(this Enum source)
    {
        return (TEnum)Enum.Parse(typeof(TEnum), source.ToString(), true);
    }

// Usage
NewEnumType newEnum = oldEnumVar.ConvertEnum<NewEnumType>();

Bedeutet das nicht, dass beide Aufzählungen dieselben numerischen Werte haben?
Kuskmen

1
Nein, dies wird nach Name und Zeichenfolge konvertiert. Enum.Foo (1) wird also in Enum2.Foo (2) übersetzt, obwohl ihre numerischen Werte unterschiedlich sind.
Justin

3
public static TEnum ConvertByName<TEnum>(this Enum source, bool ignoreCase = false) where TEnum : struct
{
    // if limited by lack of generic enum constraint
    if (!typeof(TEnum).IsEnum)
    {
        throw new InvalidOperationException("enumeration type required.");
    }

    TEnum result;
    if (!Enum.TryParse(source.ToString(), ignoreCase, out result))
    {
        throw new Exception("conversion failure.");
    }

    return result;
}

2

Ich habe vor einiger Zeit eine Reihe von Erweiterungsmethoden geschrieben, die für verschiedene Arten von Enums funktionieren . Eines funktioniert insbesondere für das, was Sie erreichen möchten, und behandelt Enums mit dem FlagsAttributesowie Enums mit verschiedenen zugrunde liegenden Typen.

public static tEnum SetFlags<tEnum>(this Enum e, tEnum flags, bool set, bool typeCheck = true) where tEnum : IComparable
{
    if (typeCheck)
    {
        if (e.GetType() != flags.GetType())
            throw new ArgumentException("Argument is not the same type as this instance.", "flags");
    }

    var flagsUnderlyingType = Enum.GetUnderlyingType(typeof(tEnum));

    var firstNum = Convert.ToUInt32(e);
    var secondNum = Convert.ToUInt32(flags);

    if (set)
        firstNum |= secondNum;

    else
        firstNum &= ~secondNum;

    var newValue = (tEnum)Convert.ChangeType(firstNum, flagsUnderlyingType);

    if (!typeCheck)
    {
        var values = Enum.GetValues(typeof(tEnum));
        var lastValue = (tEnum)values.GetValue(values.Length - 1);

        if (newValue.CompareTo(lastValue) > 0)
            return lastValue;
    }

    return newValue;
}

Von dort aus können Sie weitere spezifischere Erweiterungsmethoden hinzufügen.

public static tEnum AddFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, true);
}

public static tEnum RemoveFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
    SetFlags(e, flags, false);
}

Dieser ändert die Arten von Enums, wie Sie es versuchen.

public static tEnum ChangeType<tEnum>(this Enum e) where tEnum : IComparable
{
    return SetFlags(e, default(tEnum), true, false);
}

Seien Sie jedoch gewarnt, dass Sie mit dieser Methode zwischen allen Enumanderen konvertieren können Enum, auch wenn diese keine Flags haben. Beispielsweise:

public enum Turtle
{
    None = 0,
    Pink,
    Green,
    Blue,
    Black,
    Yellow
}

[Flags]
public enum WriteAccess : short
{
   None = 0,
   Read = 1,
   Write = 2,
   ReadWrite = 3
}

static void Main(string[] args)
{
    WriteAccess access = WriteAccess.ReadWrite;
    Turtle turtle = access.ChangeType<Turtle>();
}

Die Variable turtlehat einen Wert vonTurtle.Blue .

Bei Enumdieser Methode besteht jedoch Sicherheit vor undefinierten Werten. Zum Beispiel:

static void Main(string[] args)
{
    Turtle turtle = Turtle.Yellow;
    WriteAccess access = turtle.ChangeType<WriteAccess>();
}

In diesem Fall accesswird auf gesetzt WriteAccess.ReadWrite, da dieWriteAccess Enum einen Maximalwert von 3 hat.

Ein weiterer Nebeneffekt beim Mischen von Enums mit FlagsAttributeund ohne ist, dass der Umwandlungsprozess nicht zu einer 1: 1-Übereinstimmung zwischen ihren Werten führt.

public enum Letters
{
    None = 0,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H
}

[Flags]
public enum Flavors
{
    None = 0,
    Cherry = 1,
    Grape = 2,
    Orange = 4,
    Peach = 8
}

static void Main(string[] args)
{
    Flavors flavors = Flavors.Peach;
    Letters letters = flavors.ChangeType<Letters>();
}

In diesem Fall lettershat er einen Wert von Letters.Hstatt Letters.D, da der Hintergrundwert von Flavors.Peach8 ist. Außerdem würde eine Konvertierung von Flavors.Cherry | Flavors.Grapenach Lettersergeben Letters.C, was unintuitiv erscheinen kann.


2

Basierend auf Justins Antwort oben habe ich Folgendes gefunden:

    /// <summary>
    /// Converts Enum Value to different Enum Value (by Value Name) See https://stackoverflow.com/a/31993512/6500501.
    /// </summary>
    /// <typeparam name="TEnum">The type of the enum to convert to.</typeparam>
    /// <param name="source">The source enum to convert from.</param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public static TEnum ConvertTo<TEnum>(this Enum source)
    {
        try
        {
            return (TEnum) Enum.Parse(typeof(TEnum), source.ToString(), ignoreCase: true);
        }
        catch (ArgumentException aex)
        {
            throw new InvalidOperationException
            (
                $"Could not convert {source.GetType().ToString()} [{source.ToString()}] to {typeof(TEnum).ToString()}", aex
            );
        }
    }

1

Ich weiß, dass dies eine alte Frage ist und habe viele Antworten. Ich finde jedoch, dass die Verwendung einer switch-Anweisung wie in der akzeptierten Antwort etwas umständlich ist. Hier sind meine 2 Cent:

Meine persönliche Lieblingsmethode ist die Verwendung eines Wörterbuchs, bei dem der Schlüssel die Quellaufzählung und der Wert die Zielaufzählung ist. In dem auf die Frage gestellten Fall würde mein Code also folgendermaßen aussehen:

var genderTranslator = new Dictionary<TheirGender, MyGender>();
genderTranslator.Add(TheirGender.Male, MyGender.Male);
genderTranslator.Add(TheirGender.Female, MyGender.Female);
genderTranslator.Add(TheirGender.Unknown, MyGender.Unknown);

// translate their to mine    
var myValue = genderTranslator[TheirValue];

// translate mine to their
var TheirValue = genderTranslator .FirstOrDefault(x => x.Value == myValue).Key;;

Dies kann natürlich in eine statische Klasse eingeschlossen und als Erweiterungsmethode verwendet werden:

public static class EnumTranslator
{

    private static Dictionary<TheirGender, MyGender> GenderTranslator = InitializeGenderTranslator();

    private static Dictionary<TheirGender, MyGender> InitializeGenderTranslator()
    {
        var translator = new Dictionary<TheirGender, MyGender>();
        translator.Add(TheirGender.Male, MyGender.Male);
        translator.Add(TheirGender.Female, MyGender.Female);
        translator.Add(TheirGender.Unknown, MyGender.Unknown);
        return translator;
    }

    public static MyGender Translate(this TheirGender theirValue)
    {
        return GenderTranslator[theirValue];
    }

    public static TheirGender Translate(this MyGender myValue)
    {
        return GenderTranslator.FirstOrDefault(x => x.Value == myValue).Key;
    }

}

Ich mag diesen Ansatz, da Sie auch beide Aufzählungen aufzählen können, um das Wörterbuch zu füllen. (wenn sie natürlich in der gleichen Reihenfolge sind)
AlexS

0

Sie können ToString () verwenden, um die erste Aufzählung in ihren Namen zu konvertieren, und dann Enum.Parse (), um die Zeichenfolge zurück in die andere Aufzählung zu konvertieren. Dies löst eine Ausnahme aus, wenn der Wert von der Zielaufzählung nicht unterstützt wird (dh für einen "unbekannten" Wert).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.