C # Iterando por meio de um enum? (Indexando um System.Array)

113

Eu tenho o seguinte 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] ); 
}

No entanto, não posso indexar valores. Existe uma maneira mais fácil de fazer isso?

Ou perdi algo totalmente!

TK.
fonte

Respostas:

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

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

Ou você pode lançar o System.Array que é retornado:

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

Mas, você pode ter certeza de que GetValues ​​retorna os valores na mesma ordem em que GetNames retorna os nomes?

Frederik Gheysels
fonte
3
"Mas, você pode ter certeza de que GetValues ​​retorna os valores na mesma ordem em que GetNames retorna os nomes?" - Este é um ponto muito bom e algo que ainda não falei! Acho que sua primeira solução provavelmente forneceria uma maneira de combinar valores e strings de forma confiável
TK.
Olá, já vi menção a essa suspeita de "incompatibilidade de índice" ocorrendo ao fazer isso; no entanto, ainda não descobri se isso é realmente uma preocupação ou não? Existem casos definitivos em que essa suposição pode dar errado? THX!
Funka
Você provavelmente deseja converter o segundo "val" em um int, se quiser solucionar problemas de incompatibilidade de valores, como Enum.GetName(typeof(MyEnum), val), (int)val)em que a saída fornece o nome e o número da enumeração.
Greg
GetValues ​​e GetNames retornam na mesma ordem, a saber: "Os elementos da matriz de valor de retorno são classificados pelos valores binários das constantes enumeradas (ou seja, por sua magnitude sem sinal)."
Russell Borogove
Eu acredito que você também pode usar LINQ diretamente chamando Cast<T>o resultado:Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()...
jocull
33

Você precisa lançar a matriz - a matriz retornada é realmente do tipo solicitado, ou seja, myEnum[]se você solicitar typeof(myEnum):

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

Então values[0]etc

Marc Gravell
fonte
9

Você pode lançar esse Array para diferentes tipos de Arrays:

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

ou se você quiser os valores inteiros:

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

Você pode iterar essas matrizes lançadas, é claro :)

Arcturus
fonte
7

Que tal uma lista de dicionário?

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

e, claro, você pode alterar o tipo de valor do dicionário para quaisquer valores de enum.

mrtaikandi
fonte
Acho que esse seria o caminho a percorrer, mas infelizmente o enum está em uma área do código sobre a qual não tenho controle!
TK.
1
A única solução que encontrei que obtém os valores reais de enum inteiro!
SharpC
5

Outra solução, com possibilidades interessantes:

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;
  } 
}
Paulo
fonte
4

Aqui está outro. Precisávamos fornecer nomes amigáveis ​​para nossos EnumValues. Usamos System.ComponentModel.DescriptionAttribute para mostrar um valor de string personalizado para cada valor enum.

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

Em uso

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



    static void Main(string[] args)
    {

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

Isso acabará retornando "Algo 1", "Algo 2"

Kevin Castle
fonte
1
Só é bom se as descrições forem estáticas ... se suas descrições precisarem ser alteradas com base em uma instância, essa abordagem não funcionará.
Llyle 01 de
3

Que tal usar um loop foreach, talvez você pudesse trabalhar com isso?

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

algo assim talvez?

TWith2Sugars
fonte
Eu aprendi sobre isso ... mas preciso obter acesso a nomes e valores em 'sincronizar' ... digamos que os nomes [2] estejam emparelhados com os valores [2] e não tenho certeza de como fazer isso em um loop foreach!
TK.
Eu adicionei um exemplo - veja se isso ajuda.
TWith2Sugars
3

Pergunta antiga, mas uma abordagem um pouco mais limpa usando o LINQ .Cast<>()

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

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

Array tem um método GetValue (Int32) que você pode usar para recuperar o valor em um índice especificado.

Array.GetValue

Plástico bolha
fonte
2

Você pode simplificar isso usando strings de formato. Eu uso o seguinte snippet nas mensagens 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);
}

O especificador de formato D formata o valor enum como um decimal. Também há um especificador X que fornece saída hexadecimal.

O especificador G formata um enum como uma string. Se o atributo Flags for aplicado ao enum, os valores combinados também serão suportados. Há um especificador F que atua como se Flags estivesse sempre presente.

Veja Enum.Format ().

Frank Szczerba
fonte
2

Nos resultados de Enum.GetValues, a conversão para int produz o valor numérico. Usar ToString () produz o nome amigável. Nenhuma outra chamada para Enum.GetName é necessária.

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
fonte
0

Esta é uma maneira simples de iterar por meio de seu objeto Enum personalizado

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

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

Next
Chev
fonte
1
Acho que OP pediu C #.
jm.
0

Pergunta antiga, mas a resposta do 3Dave forneceu a abordagem mais fácil. Eu precisava de um pequeno método auxiliar para gerar um script Sql para decodificar um valor enum no banco de dados para depuração. Funcionou muito bem:

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

Eu tenho um método estático, então o uso é:

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