¿Cómo convertir de System.Enum a un entero base?

93

Me gustaría crear un método genérico para convertir cualquier tipo derivado de System.Enum a su valor entero correspondiente, sin conversión y preferiblemente sin analizar una cadena.

Por ejemplo, lo que quiero es algo como esto:

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

Pero esto no parece funcionar. Resharper informa que no puede convertir una expresión de tipo 'System.Enum' a escribir 'int'.

Ahora se me ocurrió esta solución, pero prefiero tener algo más eficiente.

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

¿Alguna sugerencia?

orj
fuente
1
Creo que es el compilador el que se queja, no Resharper.
Kugel
1
No necesariamente. Tengo un método de extensión en System.Enum, y ocasionalmente Resharper decide quejarse: No se puede convertir el tipo de argumento de instancia 'Some.Cool.Type.That.Is.An.Enum' a 'System.Enum' cuando sin duda ES una enumeración. Si compilo y ejecuto el código, funciona bien. Si luego cierro VS, destruyo el caché Resharper y lo vuelvo a encender, todo estará bien una vez que haya terminado de volver a escanear. Para mí es una especie de error de caché. Podría ser lo mismo para él.
Mir
@ Mir He tenido ReSharper "quejándose" sobre esto también. La misma solución para mí. No estoy seguro de por qué se mezclan estos tipos, pero definitivamente no es el compilador.
Akousmata

Respuestas:

134

Si no quieres emitir,

Convert.ToInt32()

podría hacer el truco.

La transmisión directa (vía (int)enumValue) no es posible. Tenga en cuenta que esto también sería "peligroso", ya que una enumeración puede tener diferentes tipos subyacentes ( int, long,byte ...).

Más formalmente: System.Enumno tiene una relación de herencia directa con Int32(aunque ambos son ValueTypes), por lo que la conversión explícita no puede ser correcta dentro del sistema de tipos

MartinStettner
fuente
2
Converter.ToInteger(MyEnum.MyEnumConstant);no le dará ningún error aquí. Edite esa parte.
nawfal
Gracias, @nawfal, tienes razón. Edité la respuesta (y aprendí algo nuevo :) ...)
MartinStettner
No funciona si el tipo subyacente difiere deint
Alex Zhukovskiy
@YaroslavSivakov no funcionará, por ejemplo, para longenum.
Alex Zhukovskiy
System.Enumes una clase, por lo que es un tipo de referencia. Los valores probablemente estén encuadrados.
Teejay
42

Lo hice funcionar lanzando a un objeto y luego a un int:

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

Esta es fea y probablemente no sea la mejor manera. Seguiré jugando con eso, para ver si puedo encontrar algo mejor ...

EDITAR: Estaba a punto de publicar que Convert.ToInt32 (enumValue) también funciona, y noté que MartinStettner se me adelantó.

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

Prueba:

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

EDITAR 2: En los comentarios, alguien dijo que esto solo funciona en C # 3.0. Acabo de probar esto en VS2005 así y funcionó:

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));
    }
BFree
fuente
Te di +1, pero esta solución solo funcionará con C # 3.0 y superior.
Vadim
Creo que esto solo satisface al compilador. ¿Conseguiste probar esto en tiempo de ejecución? No pude pasar ningún valor a esta función ...
MartinStettner
¿Porque es un método de extensión? ¿O hay algo diferente en las enumeraciones en versiones anteriores de C #?
BFree
Agregué una prueba al final de mi publicación. Intenté eso y funcionó para mí.
BFree
@BFree: Parece que solo funciona con métodos de extensión. Lo probé con una función de "estilo antiguo" (en C # 2.0) y no logré encontrar la sintaxis para pasarle ningún valor (de eso se trataba mi comentario anterior)
MartinStettner
14

Si necesita convertir cualquier enumeración a su tipo subyacente (no todas las enumeraciones están respaldadas por int), puede usar:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));
Dibujó Noakes
fuente
5
Esta es la única respuesta a prueba de balas.
Rudey
¿Es esto causa del boxeo unboxing?
b.ben
@ b.ben sí. Convert.ChangeTypeacepta objectel primer argumento y regresa object.
Drew Noakes
Sí, estoy de acuerdo con @Rudey en esto, de todos modos deberías hacer pruebas de rendimiento. La solución de BFree es, con mucho, la más rápida. Aproximadamente ocho veces más rápido. De todos modos todavía estamos hablando de garrapatas.
20 de
10

¿Por qué necesitas reinventar la rueda con un método auxiliar? Es perfectamente legal asignar un enumvalor a su tipo subyacente.

Es menos mecanografiado y, en mi opinión, más legible de usar ...

int x = (int)DayOfWeek.Tuesday;

... en lugar de algo como ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();
LukeH
fuente
6
Quiero convertir CUALQUIER valor de enumeración. Es decir, tengo una variedad de clases que tienen campos de enumeración que están siendo procesados ​​por algún código de manera genérica. Ésta es la razón por la que una conversión simple a int no es apropiada.
orj
@orj, no estoy muy seguro de a qué te refieres. ¿Puede dar un ejemplo que muestre el uso que necesita?
LukeH
2
A veces no se puede emitir directamente una enumeración, como en el caso de un método genérico. Debido a que los métodos genéricos no se pueden restringir a enumeraciones, lo ha restringido a algo como struct, que no se puede convertir en un int. En ese caso, puede usar Convert.ToInt32 (enum) para hacerlo.
Daniel T.
Me gusta la idea de que el método de extensión ToInteger () siga el mismo formato que ToString (). Creo que es menos legible lanzar a int por un lado cuando quieres el valor int y usar ToString () cuando necesitamos el valor de cadena, especialmente cuando el código está lado a lado.
Louise Eggleton
Además, el uso de un método de extensión puede agregar consistencia a su base de código. Dado que, como señaló @nawfal, hay varias formas de obtener el valor int de una enumeración, crear un método de extensión y hacer cumplir su uso significa que cada vez que obtiene el valor int de una enumeración está utilizando el mismo método
Louise Eggleton
4

De mi respuesta aquí :

Dado ecomo en:

Enum e = Question.Role;

Entonces estos funcionan:

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

Los dos últimos son feos. El primero debería ser más legible, aunque el segundo es mucho más rápido. O puede ser que un método de extensión sea lo mejor, lo mejor de ambos mundos.

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

Ahora puedes llamar:

e.GetValue<int>(); //or
e.GetIntValue();
nawfal
fuente
¿Estás seguro de que el segundo es más rápido? Supongo que encuadrará la enumeración y luego volverá a desempaquetar a int.
Yoyo
1
@yoyo la evariable ya tiene la instancia en caja del tipo de valor real, es decir, enum. Cuando escribe Enum e = Question.Role;, ya está en una caja. La pregunta es sobre convertir el recuadro System.Enumen el tipo int subyacente (unboxing). Así que aquí el desembalaje es solo una cuestión de rendimiento. (int)(object)ees una llamada directa de unbox; sí debería ser más rápido que otros enfoques. Ver esto
nawfal
gracias por la explicación. Tenía la impresión errónea de que una instancia de System.Enum era un valor sin caja. Vea esto también - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo
@yoyo hmm sí. La cita relevante del enlace: De cualquier tipo de enumeración al tipo System.Enum.
nawfal
1

Transmitir de una System.Enuma una intfunciona bien para mí (también está en MSDN ). Quizás sea un error de Resharper.

jpoh
fuente
1
¿Qué versión del tiempo de ejecución estás usando?
JoshBerke
2
el enlace muestra el casting de subtipos de System.Enum, no él System.Enummismo.
nawfal
Un problema similar que tuve fue un error de caché Resharper. Cerrar VS y eliminar la caché de Resharper hizo que el error desapareciera, incluso después de una nueva exploración completa.
Mir
1

Dado que las enumeraciones están restringidas a byte, sbyte, short, ushort, int, uint, long y ulong, podemos hacer algunas suposiciones.

Podemos evitar excepciones durante la conversión utilizando el contenedor más grande disponible. Desafortunadamente, no está claro qué contenedor usar porque ulong aumentará para números negativos y long aumentará para números entre long.MaxValue y ulong.MaxValue. Necesitamos cambiar entre estas opciones según el tipo subyacente.

Por supuesto, aún debe decidir qué hacer cuando el resultado no encaja dentro de un int. Creo que el casting está bien, pero todavía hay algunos errores:

  1. para enumeraciones basadas en un tipo con un espacio de campo mayor que int (long y ulong), es posible que algunas enumeraciones evalúen el mismo valor.
  2. emitir un número mayor que int.MaxValue arrojará una excepción si se encuentra en una región marcada.

Aquí está mi sugerencia, dejaré que el lector decida dónde exponer esta función; como ayudante o extensión.

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

No olvide que el tipo Enum en sí tiene un montón de funciones auxiliares estáticas. Si todo lo que quiere hacer es convertir una instancia de la enumeración a su tipo entero correspondiente, entonces la conversión es probablemente la forma más eficiente.

Creo que ReSharper se queja porque Enum no es una enumeración de ningún tipo en particular, y las enumeraciones en sí se derivan de un tipo de valor escalar, no de Enum. Si necesita una conversión adaptable de una manera genérica, diría que esto podría adaptarse bien a usted (tenga en cuenta que el tipo de enumeración en sí también se incluye en el genérico:

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

Esto podría usarse así:

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

// ...

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

No puedo decirlo con certeza, pero el código anterior incluso podría ser compatible con la inferencia de tipo de C #, lo que permite lo siguiente:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);
jrista
fuente
1
Desafortunadamente, en su ejemplo, E puede ser de cualquier tipo. Los genéricos no me permiten usar Enum como restricción de parámetro de tipo. No puedo decir: donde T: Enum
Vadim
Sin embargo, podrías usar where T: struct. No es tan estricto como quieres, pero eliminaría cualquier tipo de referencia (que creo que es lo que estás tratando de hacer)
jrista
puede usar: if (! typeof (E) .IsEnum) throw new ArgumentException ("E debe ser un tipo enumerado");
diadiora
1
Esto no es ni remotamente correcto, el ayudante estático ni siquiera es válido.
Nicholi
1
¿Por qué debería alguien usar en EnumHelpers.Convert<int, StopLight>(StopLight.Red);lugar de (int)StopLight.Read;? También se habla de la pregunta System.Enum.
nawfal