2008-12-09 6 views
9

Diversión con enumeraciones en C#. Tome una lista genérica que se crea para almacenar algunos Enum que había definido previamente y agregue algunos elementos en él. Itera con foreach o GetEnumerator<T>(), pero especifique alguna otra enumeración luego el original y vea qué sucede. Esperaba InvalidCastException o algo así, pero funciona perfectamente :).¿Las enumeraciones solo se llaman enteros, tipos o ninguno de los dos?

Para la ilustración tomemos una aplicación de consola sencilla y crear dos enumeraciones allí: Coches y Animales:

public enum Cars 
    { 
     Honda = 0, 
     Toyota = 1, 
     Chevrolet = 2 
    } 
    public enum Animals 
    { 
     Dog = 0, 
     Cat = 1, 
     Tiger = 2 
    } 

y hacer esto en el método principal:

public static void Main() 
    { 
     List<Cars> cars = new List<Cars>(); 
     List<Animals> animals = new List<Animals>(); 
     cars.Add(Cars.Chevrolet); 
     cars.Add(Cars.Honda); 
     cars.Add(Cars.Toyota); 

     foreach (Animals isItACar in cars) 
     { 
      Console.WriteLine(isItACar.ToString()); 
     } 
     Console.ReadLine(); 
    } 

se imprimirá este en la consola:

Tiger 
Dog 
Cat 

¿Por qué sucede esto? Mi primera suposición fue que enum no es realmente un Type por sí mismo, es justo e int, pero eso no es verdad: si escribimos:

Console.WriteLine(Animals.Tiger.GetType().FullName); ¡Imprimiremos su nombre completo! ¿Entonces por qué esto?

Respuesta

21

Los tipos de Enum son distintos, pero estás confundido por un lanzamiento implícito que está en foreach.

Vamos a reescribir su bucle un poco:

public static void Main() 
{ 
    List<Cars> cars = new List<Cars>(); 
    List<Animals> animals = new List<Animals>(); 
    cars.Add(Cars.Chevrolet); 
    cars.Add(Cars.Honda); 
    cars.Add(Cars.Toyota); 

    foreach (Cars value in cars) 
    { 
     // This time the cast is explicit. 
     Animals isItACar = (Animals) value; 
     Console.WriteLine(isItACar.ToString()); 
    } 
    Console.ReadLine(); 
} 

Ahora bien, ¿el resultado sorprende? Esperemos que no, excepto posiblemente el hecho de que puede emitir de una enumeración a otra. Esta es solo una versión más explícita de lo que está haciendo su código original.

El hecho de que haya un reparto implícito en cada uno de los bucles foreach (aunque normalmente no es operativo) es la parte que la mayoría de los desarrolladores considerarían confusa, creo.

De la sección 8.8.4 de la C# 3.0 spec:

los pasos anteriores, si tiene éxito, producen de forma inequívoca un tipo de colección C, tipo empadronador E y el elemento tipo T.Una declaración foreach de la forma

foreach (V v in x) embedded-statement 

se expande entonces a:

{ 
    E e = ((C)(x)).GetEnumerator(); 
    try { 
     V v; 
     while (e.MoveNext()) { 
      v = (V)(T)e.Current; 
      embedded-statement 
     } 
    } 
    finally { 
     ... // Dispose e 
    } 
} 

La conversión enumeración en sí está cubierto en la sección 6.2.2:

Las conversiones de enumeración explícita son:

  • De sbyte, byte, short, ushort, int, uint larga, ulong, char, float, double o decimal a cualquier tipo de enumeración.
  • Desde cualquier tipo de enum hasta sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double o decimal.
  • Desde cualquier tipo de enumeración a cualquier otro tipo de enumeración.

Una conversión enumeración explícita entre dos tipos son procesados ​​por el tratamiento de cualquier participante enum de tipo como el tipo subyacente de que enum de tipo, y luego realizar una implícita o explícita numérico conversión entre los tipos resultantes. Por ejemplo, dado un tipo de enumeración E con un tipo subyacente de int, una conversión de de E a byte se procesa como una conversión numérica explícita (§6.2.1) de int a byte, y una conversión de byte a E se procesa como una conversión numérica implícita (§6.1.2) de byte a int.

+1

Sí muy buena respuesta. Ahora me doy cuenta de que el molde explícito entre enums de cualquier tipo no fallaría ya que son prácticamente enteros, y estaba la raíz del problema. – Aleksandar

2

Conceptualmente, un Enum es un valor estáticamente estátizado con una representación de cadena y un número. Cuando llame al ToString() en una enumeración, devolverá la representación de cadena. Cuando llame al GetType() en una enumeración, obtendrá el tipo de enumeración estática. Si lanzas una enumeración al int, obtendrás el valor entero para la enumeración.

Se supone que los subtítulos tienen caracteres fuertes, pero hay algunas cosas que debe tener en cuenta, como el hecho de que cualquier número entero se puede convertir en cualquier enumeración incluso si no tiene una declaración correspondiente (en cuyo caso la representación de la cadena será la misma que la del número).

En el CLR, las enumeraciones (como bool s) solo se tratan como int s, pero si llama a GetType() o GetString() llama a las versiones que hacen lo que se describió anteriormente.

0

También puede derivar una enumeración de un tipo específico.

public enum Cats : byte { ... } 
public enum Dogs : int { ... } 
Cuestiones relacionadas