convertir una enumeración en otro tipo de enumeración


120

Tengo una enumeración de, por ejemplo, ' Gender' ( Male =0 , Female =1) y tengo otra enumeración de un servicio que tiene su propia enumeración de género ( Male =0 , Female =1, Unknown =2)

Mi pregunta es ¿cómo puedo escribir algo rápido y agradable para convertir de su enumeración a la mía?


6
¿A qué quieres convertir "desconocido"?
Pavel Minaev

Puede encasillar la enumeración a otros tipos de enumeración cuando ambos tienen los mismos valores, consulte ideone.com/7lgvgf
Gowtham S

Respuestas:


87

El uso de un método de extensión funciona bastante bien, cuando se utilizan los dos métodos de conversión sugeridos por 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
    }
}

Obviamente, no es necesario utilizar clases separadas si no lo desea. Mi preferencia es mantener los métodos de extensión agrupados por las clases / estructuras / enumeraciones a las que se aplican.


233

Dado Enum1 value = ..., entonces si te refieres por nombre:

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

Si te refieres al valor numérico, por lo general solo puedes lanzar:

Enum2 value2 = (Enum2)value;

(con el elenco, es posible que desee usar Enum.IsDefinedpara verificar valores válidos)


16
Esta es la mejor respuesta
Nicholas

1
Aquí hay una versión que usa Enum.Tryparse: Enum2 value2 = Enum.TryParse(value.ToString(), out Enum2 outValue) ? outValue : Enum2.Unknown; Esto le permitirá manejar valores de entrada que no existen Enum2sin necesidad de llamar Enum.IsDefinedo atrapar ArgumentExceptionmensajes de correo electrónico Enum.Parse. Tenga en cuenta que el orden de los parámetros es más o menos inverso Enum.Parse.
Sander

47

Simplemente envíe uno a int y luego a la otra enumeración (considerando que desea que el mapeo se realice en función del valor):

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

3
Aunque es poco probable que lo vea 'en la naturaleza', y es muy poco probable que sea el caso de los géneros, podría existir alguna enumeración que esté respaldada por un long(o ulong) en lugar de un intque tenga miembros definidos que estén arriba int.MaxValue(o abajo) int.MinValue), en cuyo caso la conversión a intpodría desbordarse y terminaría con un valor de enumeración indefinido que debería definirse.
Rich O'Kelly

por supuesto. la forma correcta sería (Gender2) ((inserte el tipo subyacente aquí) gender1) pero creo que el ejemplo anterior da la idea correcta, así que no la cambiaré.
Adrian Zanescu

3
Esto requiere que las dos enumeraciones tengan los mismos valores en el mismo orden. Si bien resuelve este problema específico, esto es realmente frágil y no lo usaría para el mapeo de enumeraciones en general.
sonicblis

2
bueno ... ¡duh! . El mapeo debe hacerse basándose en algo. En este caso, el mapeo está en valor integral. Para mapear la base en el nombre, necesita un código diferente. Para otro tipo de mapeo, algo más. Nadie dijo que esto es "para el mapeo de enumeración en general" y ese caso no existe a menos que pueda intentar especificar qué significa "mapeo en general"
Adrian Zanescu

20

Para ser minucioso, normalmente creo un par de funciones, una que toma Enum 1 y devuelve Enum 2 y otra que toma Enum 2 y devuelve Enum 1. Cada una consta de una declaración de caso que asigna entradas a salidas y el caso predeterminado arroja una excepción con un mensaje quejándose de un valor inesperado.

En este caso particular, podría aprovechar el hecho de que los valores enteros de Masculino y Femenino son los mismos, pero lo evitaría ya que es pirateado y está sujeto a roturas si alguna de las enumeraciones cambia en el futuro.


7
+1 He visto a muchos desarrolladores renunciar a la tentación de utilizar valores enteros de enumeraciones para convertirlos, pero esto es muy propenso a errores. El método de la vieja escuela de escribir 2 funciones ha demostrado su valor a lo largo del tiempo ...
Hemant

20

Si tenemos:

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

y

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

Podemos hacer con seguridad

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

O incluso

var enumOfGender2Type = (Gender2)0;

Si desea cubrir el caso en el que una enumeración en el lado derecho del signo '=' tiene más valores que la enumeración en el lado izquierdo, tendrá que escribir su propio método / diccionario para cubrir eso, como sugirieron otros.


¡¿Tu respuesta es como hacer una pregunta ?! Si es así, esta no es una respuesta y si no, hay una respuesta similar arriba ;).
shA.t

13

Podría escribir un método de extensión genérico simple como este

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
No cubre el caso de valores perdidos como se sugiere en las respuestas anteriores. Debería modificar este método de extensión cubriendo ese caso también.
eRaisedToX

8

podría escribir una función simple como la siguiente:

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
esto no es una función. esperado 'MyGender' y usted regresa 'void'
bl4ckr0se

7

Aquí hay una versión del método de extensión si alguien está interesado

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

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

¿No implica eso que ambas enumeraciones tienen los mismos valores numéricos?
kuskmen

1
No, esto es convertir por nombre por cadena. Entonces Enum.Foo (1) se traducirá a Enum2.Foo (2) aunque sus valores numéricos sean diferentes.
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

Escribí un conjunto de métodos de extensión hace un tiempo que funcionan para varios tipos diferentes de Enums. Uno en particular funciona para lo que está tratando de lograr y maneja Enums con FlagsAttributeasí como Enums con diferentes tipos subyacentes.

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

Desde allí, puede agregar otros métodos de extensión más específicos.

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

Este cambiará los tipos de mensajes de correo Enumelectrónico como está intentando hacer.

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

Tenga en cuenta, sin embargo, que PUEDE convertir entre cualquiera Enumy cualquier otro Enumutilizando este método, incluso aquellos que no tienen banderas. Por ejemplo:

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 variable turtletendrá un valor de Turtle.Blue.

Sin embargo, existe seguridad frente a Enumvalores indefinidos con este método. Por ejemplo:

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

En este caso, accessse establecerá en WriteAccess.ReadWrite, ya que WriteAccess Enumtiene un valor máximo de 3.

Otro efecto secundario de mezclar Enums con FlagsAttributey sin él es que el proceso de conversión no dará como resultado una coincidencia de 1 a 1 entre sus valores.

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

En este caso, letterstendrá un valor de en Letters.Hlugar de Letters.D, ya que el valor de respaldo de Flavors.Peaches 8. Además, una conversión de Flavors.Cherry | Flavors.Grapea Lettersproduciría Letters.C, lo que puede parecer poco intuitivo.


2

Basado en la respuesta de Justin anterior, se me ocurrió esto:

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

Sé que es una pregunta antigua y tengo muchas respuestas.Sin embargo, encuentro que usar una declaración de cambio como en la respuesta aceptada es algo engorroso, así que aquí están mis 2 centavos:

Mi método favorito personal es usar un diccionario, donde la clave es la enumeración de origen y el valor es la enumeración de destino, por lo que en el caso presentado en la pregunta, mi código se vería así:

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

Por supuesto, esto se puede envolver en una clase estática y usarse como métodos de extensión:

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

}

Me gusta este enfoque, ya que también puede enumerar ambas enumeraciones para completar el diccionario. (cuando están en el mismo orden, por supuesto)
AlexS

0

Puede usar ToString () para convertir la primera enumeración a su nombre, y luego Enum.Parse () para convertir la cadena de nuevo a la otra enumeración. Esto generará una excepción si el valor no es compatible con la enumeración de destino (es decir, para un valor "Desconocido")

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.