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.