convertire un enum in un altro tipo di enum


120

Ho un'enumerazione ad esempio di ' Gender' ( Male =0 , Female =1) e un'altra enumerazione da un servizio che ha la propria enumerazione Gender ( Male =0 , Female =1, Unknown =2)

La mia domanda è come posso scrivere qualcosa di veloce e carino da convertire dal loro enum al mio?


6
In cosa vuoi convertire "sconosciuto"?
Pavel Minaev

Puoi trasformare l'enumerazione in altri tipi di enumerazione
Gowtham S

Risposte:


87

L'uso di un metodo di estensione funziona abbastanza bene, quando si utilizzano i due metodi di conversione suggeriti da Nate:

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
    }
}

Ovviamente non c'è bisogno di usare classi separate se non vuoi. La mia preferenza è mantenere i metodi di estensione raggruppati in base alle classi / strutture / enumerazioni a cui si applicano.


233

Dato Enum1 value = ..., quindi se intendi per nome:

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

Se intendi per valore numerico, di solito puoi semplicemente trasmettere:

Enum2 value2 = (Enum2)value;

(con il cast, potresti voler usare Enum.IsDefinedper verificare valori validi, però)


16
Questa è la risposta migliore
Nicholas

1
Ecco una versione che utilizza Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; Ciò ti consentirà di gestire i valori di input che non esistono Enum2senza la necessità di chiamare Enum.IsDefinedo catturare ArgumentExceptioni messaggi lanciati da Enum.Parse. Notare che l'ordine dei parametri è più o meno invertito da Enum.Parse.
Sander

47

Basta lanciarne uno su int e poi lanciarlo sull'altro enum (considerando che vuoi che la mappatura venga eseguita in base al valore):

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

3
Sebbene sia improbabile vederlo `` in natura '', ed è altamente improbabile che sia il caso per i sessi, potrebbe esistere qualche enum che è supportato da un long(o ulong) piuttosto che da un intche ha membri definiti che sono sopra int.MaxValue(o sotto int.MinValue), nel qual caso il cast di intpotrebbe traboccare e si finirebbe con un valore enum non definito che dovrebbe essere definito.
Rich O'Kelly

ovviamente. il modo corretto sarebbe (Gender2) ((inserisci il tipo sottostante qui) genere1) ma penso che l'esempio sopra dia l'idea giusta, quindi non lo cambierò.
Adrian Zanescu

3
Ciò richiede che le due enumerazioni abbiano gli stessi valori nello stesso ordine. Sebbene risolva questo problema specifico, è davvero fragile e non lo userei per la mappatura enum in generale.
sonicblis

2
beh .... duh! . La mappatura deve essere eseguita in base a qualcosa. In questo caso la mappatura è su valore integrale. Per la mappatura in base al nome è necessario un codice diverso. Per un altro tipo di mappatura qualcos'altro. Nessuno ha detto che questo è "per la mappatura enum in generale" e quel caso non esiste a meno che tu non possa provare a specificare cosa significa "mappatura in generale"
Adrian Zanescu

20

Per essere accurato, normalmente creo una coppia di funzioni, una che accetta Enum 1 e restituisce Enum 2 e un'altra che accetta Enum 2 e restituisce Enum 1. Ciascuna consiste in un'istruzione case che mappa gli input agli output e il caso predefinito genera un'eccezione con un messaggio che si lamenta di un valore inaspettato.

In questo caso particolare potresti trarre vantaggio dal fatto che i valori interi di Male e Female sono gli stessi, ma lo eviterei poiché è hacker e soggetto a rotture se l'enum cambia in futuro.


7
+1 Ho visto molti sviluppatori rinunciare all'impulso di utilizzare il valore intero di enums per convertirli, ma questo è molto soggetto a errori. Il metodo della vecchia scuola di scrivere 2 funzioni ha dimostrato il suo valore nel tempo ...
Hemant

20

Se abbiamo:

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

e

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

Possiamo tranquillamente fare

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

O anche

var enumOfGender2Type = (Gender2)0;

Se vuoi coprire il caso in cui un'enumerazione a destra del segno '=' ha più valori dell'enum a sinistra, dovrai scrivere il tuo metodo / dizionario per coprirlo come altri hanno suggerito.


La tua risposta è come fare una domanda !? Se sì questa non è una risposta e se no c'è una risposta simile sopra ;).
shA. Il

13

Potresti scrivere un semplice metodo di estensione generico come questo

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
Non copre il caso di valori mancanti come suggerito nelle risposte precedenti. Dovresti modificare questo metodo di estensione anche per quel caso.
eRaisedToX

8

potresti scrivere una semplice funzione come la seguente:

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
questa non è una funzione. previsto "MyGender" e tu ritorni "void"
bl4ckr0se

7

Ecco una versione del metodo di estensione se qualcuno è interessato

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

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

Ciò non implica che entrambe le enumerazioni abbiano gli stessi valori numerici?
kuskmen

1
No, questa è la conversione per nome per stringa. Quindi Enum.Foo (1) si tradurrà in Enum2.Foo (2) anche se i loro valori numerici sono diversi.
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

Qualche tempo fa ho scritto un set di metodi di estensione che funzionano per diversi tipi di Enums. Uno in particolare opere per quello che si sta cercando di realizzare e maniglie Enums con le FlagsAttributecosì come Enums con diversi tipi sottostanti.

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;
}

Da lì puoi aggiungere altri metodi di estensione più specifici.

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);
}

Questo cambierà i tipi di messaggi Enumcome stai cercando di fare.

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

Tieni presente, tuttavia, che PUOI convertire tra qualsiasi Enume qualsiasi altro Enumutilizzando questo metodo, anche quelli che non hanno flag. Per esempio:

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>();
}

La variabile turtleavrà un valore di Turtle.Blue.

Tuttavia, c'è sicurezza da Enumvalori indefiniti usando questo metodo. Per esempio:

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

In questo caso, accesssarà impostato a WriteAccess.ReadWrite, poiché WriteAccess Enumha un valore massimo di 3.

Un altro effetto collaterale del mescolare Enumi messaggi con FlagsAttributee quelli senza è che il processo di conversione non si tradurrà in una corrispondenza 1 a 1 tra i loro valori.

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 questo caso, lettersavrà un valore di Letters.Hinvece di Letters.D, poiché il valore di supporto di Flavors.Peachè 8. Inoltre, una conversione da Flavors.Cherry | Flavors.Grapea Lettersprodurrebbe Letters.C, il che può sembrare non intuitivo.


2

Sulla base della risposta di Justin sopra, ho pensato a questo:

    /// <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

So che è una vecchia domanda e ho molte risposte, tuttavia trovo che l'utilizzo di un'istruzione switch come nella risposta accettata sia alquanto macchinoso, quindi ecco i miei 2 centesimi:

Il mio metodo preferito personale è usare un dizionario, dove la chiave è l'enumerazione di origine e il valore è l'enumerazione di destinazione - quindi nel caso presentato sulla domanda il mio codice sarebbe simile a questo:

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;;

Naturalmente, questo può essere racchiuso in una classe statica ed essere utilizzato come metodi di estensione:

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;
    }

}

Mi piace questo approccio poiché puoi anche enumerare entrambe le enumerazioni per popolare il dizionario. (quando sono nello stesso ordine ovviamente)
AlexS

0

È possibile utilizzare ToString () per convertire la prima enum nel suo nome e quindi Enum.Parse () per riconvertire la stringa nell'altra Enum. Questo genererà un'eccezione se il valore non è supportato dall'enumerazione di destinazione (cioè per un valore "Sconosciuto")

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.