C # ¿Iterando a través de una enumeración? (Indexación de una matriz de sistema)

113

Tengo el siguiente código:

// Obtain the string names of all the elements within myEnum 
String[] names = Enum.GetNames( typeof( myEnum ) );

// Obtain the values of all the elements within myEnum 
Array values = Enum.GetValues( typeof( myEnum ) );

// Print the names and values to file
for ( int i = 0; i < names.Length; i++ )
{
    print( names[i], values[i] ); 
}

Sin embargo, no puedo indexar valores. ¿Hay una manera más fácil de hacer esto?

¡O me he perdido algo por completo!

TK.
fuente

Respuestas:

204
Array values = Enum.GetValues(typeof(myEnum));

foreach( MyEnum val in values )
{
   Console.WriteLine (String.Format("{0}: {1}", Enum.GetName(typeof(MyEnum), val), val));
}

O puede convertir el System.Array que se devuelve:

string[] names = Enum.GetNames(typeof(MyEnum));
MyEnum[] values = (MyEnum[])Enum.GetValues(typeof(MyEnum));

for( int i = 0; i < names.Length; i++ )
{
    print(names[i], values[i]);
}

Pero, ¿puede estar seguro de que GetValues ​​devuelve los valores en el mismo orden en que GetNames devuelve los nombres?

Frederik Gheysels
fuente
3
"Pero, ¿puede estar seguro de que GetValues ​​devuelve los valores en el mismo orden en que GetNames devuelve los nombres?" - ¡Este es un punto muy bueno y algo que todavía tengo que abordar! Creo que su primera solución probablemente proporcionaría una forma de hacer coincidir valores y cadenas de manera confiable
TK.
Hola, he visto una mención antes de esta sospecha de "desajuste de índice" que ocurre al hacer esto; sin embargo, todavía tengo que descubrir si esto realmente es una preocupación o no. ¿Existen casos definitivos en los que esta suposición pueda salir mal? ¡Gracias!
Funka
Probablemente desee convertir el segundo "val" en un int, si desea solucionar la falta de coincidencia de valores, como en el caso Enum.GetName(typeof(MyEnum), val), (int)val)de que la salida proporcione el nombre y el número de enumeración.
Greg
GetValues ​​y GetNames regresan en el mismo orden, a saber: "Los elementos de la matriz de valor de retorno se ordenan por los valores binarios de las constantes enumeradas (es decir, por su magnitud sin signo)".
Russell Borogove
Creo que también puede usar LINQ directamente llamando Cast<T>al resultado:Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()...
jocull
33

Debe convertir la matriz; la matriz devuelta es en realidad del tipo solicitado, es decir, myEnum[]si solicita typeof(myEnum):

myEnum[] values = (myEnum[]) Enum.GetValues(typeof(myEnum));

Entonces, values[0]etc.

Marc Gravell
fuente
9

Puede convertir esa matriz a diferentes tipos de matrices:

myEnum[] values = (myEnum[])Enum.GetValues(typeof(myEnum));

o si quieres los valores enteros:

int[] values = (int[])Enum.GetValues(typeof(myEnum));

Puede iterar esas matrices fundidas, por supuesto :)

Arcturus
fuente
7

¿Qué tal una lista de diccionarios?

Dictionary<string, int> list = new Dictionary<string, int>();
foreach( var item in Enum.GetNames(typeof(MyEnum)) )
{
    list.Add(item, (int)Enum.Parse(typeof(MyEnum), item));
}

y, por supuesto, puede cambiar el tipo de valor del diccionario a cualquiera que sea su enumeración.

mrtaikandi
fuente
Creo que este sería el camino a seguir, ¡pero desafortunadamente la enumeración está en un área de código sobre la que no tengo control!
TK.
1
¡La única solución que he encontrado que obtiene los valores de enumeración enteros reales!
SharpC
5

Otra solución, con interesantes posibilidades:

enum Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }

static class Helpers
{
public static IEnumerable<Days> AllDays(Days First)
{
  if (First == Days.Monday)
  {
     yield return Days.Monday;
     yield return Days.Tuesday;
     yield return Days.Wednesday;
     yield return Days.Thursday;
     yield return Days.Friday;
     yield return Days.Saturday;
     yield return Days.Sunday;
  } 

  if (First == Days.Saturday)
  {
     yield return Days.Saturday;
     yield return Days.Sunday;
     yield return Days.Monday;
     yield return Days.Tuesday;
     yield return Days.Wednesday;
     yield return Days.Thursday;
     yield return Days.Friday;
  } 
}
Pablo
fuente
4

Aquí está otro. Tuvimos la necesidad de proporcionar nombres descriptivos para nuestros EnumValues. Usamos System.ComponentModel.DescriptionAttribute para mostrar un valor de cadena personalizado para cada valor de enumeración.

public static class StaticClass
{
    public static string GetEnumDescription(Enum currentEnum)
    {
        string description = String.Empty;
        DescriptionAttribute da;

        FieldInfo fi = currentEnum.GetType().
                    GetField(currentEnum.ToString());
        da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
                    typeof(DescriptionAttribute));
        if (da != null)
            description = da.Description;
        else
            description = currentEnum.ToString();

        return description;
    }

    public static List<string> GetEnumFormattedNames<TEnum>()
    {
        var enumType = typeof(TEnum);
        if (enumType == typeof(Enum))
            throw new ArgumentException("typeof(TEnum) == System.Enum", "TEnum");

        if (!(enumType.IsEnum))
            throw new ArgumentException(String.Format("typeof({0}).IsEnum == false", enumType), "TEnum");

        List<string> formattedNames = new List<string>();
        var list = Enum.GetValues(enumType).OfType<TEnum>().ToList<TEnum>();

        foreach (TEnum item in list)
        {
            formattedNames.Add(GetEnumDescription(item as Enum));
        }

        return formattedNames;
    }
}

En uso

 public enum TestEnum
 { 
        [Description("Something 1")]
        Dr = 0,
        [Description("Something 2")]
        Mr = 1
 }



    static void Main(string[] args)
    {

        var vals = StaticClass.GetEnumFormattedNames<TestEnum>();
    }

Esto terminará mostrando "Algo 1", "Algo 2"

Kevin Castle
fuente
1
Solo es bueno si las descripciones son estáticas ... si sus descripciones deben cambiar en función de una instancia, este enfoque no funcionará.
Llyle
3

¿Qué hay de usar un bucle foreach, tal vez podrías trabajar con eso?

  int i = 0;
  foreach (var o in values)
  {
    print(names[i], o);
    i++;
  }

tal vez algo así?

TWith2Sugars
fuente
Tengo algo de eso ... pero necesito tener acceso tanto a los nombres como a los valores en 'sincronización' ... digamos que los nombres [2] están emparejados con los valores [2] y no estoy seguro de cómo lograr esto en un bucle foreach!
TK.
Agregué un ejemplo, vea si eso ayuda.
TWith2Sugars
3

Pregunta antigua, pero un enfoque un poco más limpio con LINQ .Cast<>()

var values = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();

foreach(var val in values)
{
    Console.WriteLine("Member: {0}",val.ToString());     
}
3Dave
fuente
2

Array tiene un método GetValue (Int32) que puede usar para recuperar el valor en un índice específico.

Array.GetValue

Plástico de burbujas
fuente
2

Puede simplificar esto usando cadenas de formato. Utilizo el siguiente fragmento en los mensajes de uso:

writer.WriteLine("Exit codes are a combination of the following:");
foreach (ExitCodes value in Enum.GetValues(typeof(ExitCodes)))
{
    writer.WriteLine("   {0,4:D}: {0:G}", value);
}

El especificador de formato D formatea el valor de enumeración como decimal. También hay un especificador X que proporciona una salida hexadecimal.

El especificador G formatea una enumeración como una cadena. Si el atributo Flags se aplica a la enumeración, también se admiten los valores combinados. Hay un especificador F que actúa como si Flags estuviera siempre presente.

Consulte Enum.Format ().

Frank Szczerba
fuente
2

En los resultados de Enum.GetValues, la conversión a int produce el valor numérico. El uso de ToString () produce el nombre descriptivo. No se necesitan otras llamadas a Enum.GetName.

public enum MyEnum
{
    FirstWord,
    SecondWord,
    Another = 5
};

// later in some method  

 StringBuilder sb = new StringBuilder();
 foreach (var val in Enum.GetValues(typeof(MyEnum))) {
   int numberValue = (int)val;
   string friendyName = val.ToString();
   sb.Append("Enum number " + numberValue + " has the name " + friendyName + "\n");
 }
 File.WriteAllText(@"C:\temp\myfile.txt", sb.ToString());

 // Produces the output file contents:
 /*
 Enum number 0 has the name FirstWord
 Enum number 1 has the name SecondWord
 Enum number 5 has the name Another
 */
Mike Wodarczyk
fuente
0

Aquí hay una forma sencilla de iterar a través de su objeto Enum personalizado

For Each enumValue As Integer In [Enum].GetValues(GetType(MyEnum))

     Print([Enum].GetName(GetType(MyEnum), enumValue).ToString)

Next
Chev
fuente
1
Creo que OP pidió C #.
jm.
0

Pregunta antigua, pero la respuesta de 3Dave proporcionó el enfoque más fácil. Necesitaba un pequeño método auxiliar para generar un script Sql para decodificar un valor de enumeración en la base de datos para la depuración. Funcionó muy bien:

    public static string EnumToCheater<T>() {
        var sql = "";
        foreach (var enumValue in Enum.GetValues(typeof(T)))
            sql += $@"when {(int) enumValue} then '{enumValue}' ";
        return $@"case ?? {sql}else '??' end,";
    }

Lo tengo en un método estático, por lo que el uso es:

var cheater = MyStaticClass.EnumToCheater<MyEnum>()
Wade Hatler
fuente