C # Iterowanie przez wyliczenie? (Indeksowanie tablicy System.Array)

113

Mam następujący kod:

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

Nie mogę jednak indeksować wartości. Czy jest na to łatwiejszy sposób?

A może całkowicie przegapiłem coś!

TK.
źródło

Odpowiedzi:

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

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

Lub możesz rzutować zwracaną tablicę System.Array:

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

Ale czy możesz być pewien, że GetValues ​​zwraca wartości w tej samej kolejności, w jakiej GetNames zwraca nazwy?

Frederik Gheysels
źródło
3
„Ale czy możesz być pewien, że GetValues ​​zwraca wartości w tej samej kolejności, w jakiej GetNames zwraca nazwy?” - To bardzo dobry punkt, o którym jeszcze nie wspomniałem! Myślę, że Twoje pierwsze rozwiązanie prawdopodobnie zapewniłoby sposób niezawodnego dopasowania wartości i ciągów
TK.
Witam, widziałem wcześniej wzmiankę o podejrzeniu „niezgodności indeksów” występującym podczas wykonywania tej czynności; jednak nie odkryłem jeszcze, czy to naprawdę jest problem, czy nie? Czy są jakieś ostateczne przypadki, w których to założenie może się nie udać? Dzięki!
Funka
Prawdopodobnie chcesz rzutować drugie „val” na int, jeśli chcesz rozwiązać problem z niezgodnością wartości, tak jak w przypadku, Enum.GetName(typeof(MyEnum), val), (int)val)gdy dane wyjściowe zawierają nazwę i numer wyliczenia.
Greg,
GetValues ​​i GetNames zwracają się w tej samej kolejności, a mianowicie: „Elementy tablicy wartości zwracanych są sortowane według wartości binarnych wyliczonych stałych (to znaczy według ich wielkości bez znaku)”.
Russell Borogove
Uważam, że możesz również użyć LINQ bezpośrednio, dzwoniąc Cast<T>na wynik:Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()...
żart
33

Musisz rzutować tablicę - zwracana tablica jest faktycznie żądanego typu, tj. myEnum[]Jeśli pytasz o typeof(myEnum):

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

Następnie values[0]itp

Marc Gravell
źródło
9

Możesz rzutować tę tablicę na różne typy tablic:

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

lub jeśli chcesz wartości całkowite:

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

Oczywiście możesz iterować te rzutowane tablice :)

Arcturus
źródło
7

A może lista słowników?

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

i oczywiście możesz zmienić typ wartości słownika na dowolne wartości wyliczenia.

mrtaikandi
źródło
Myślę, że to byłaby droga, ale niestety wyliczenie znajduje się w obszarze kodu, nad którym nie mam kontroli!
TK.
1
Jedyne rozwiązanie, które znalazłem, które pobiera rzeczywiste wartości liczb całkowitych!
SharpC,
5

Kolejne rozwiązanie, z ciekawymi możliwościami:

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;
  } 
}
Paweł
źródło
4

Oto inny. Musieliśmy podać przyjazne nazwy dla naszych wartości EnumValues. Użyliśmy System.ComponentModel.DescriptionAttribute, aby wyświetlić niestandardową wartość ciągu dla każdej wartości wyliczenia.

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

W użyciu

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



    static void Main(string[] args)
    {

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

To zakończy zwracanie „Coś 1”, „Coś 2”

Zamek Kevina
źródło
1
Dobrze, jeśli opisy są statyczne ... jeśli opisy muszą zostać zmienione na podstawie instancji, to podejście nie zadziała.
Llyle
3

A co z używaniem pętli foreach, może mógłbyś z tym popracować?

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

może coś takiego?

TWith2Sugars
źródło
Myślałem o tym ... ale muszę uzyskać dostęp do nazw i wartości w „synchronizacji”… powiedzmy, że nazwy [2] są połączone z wartościami [2] i nie jestem pewien, jak to osiągnąć w pętla foreach!
TK.
Dodałem przykład - zobacz, czy to pomaga.
TWith2Sugars
3

Stare pytanie, ale nieco czystsze podejście przy użyciu LINQ .Cast<>()

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

foreach(var val in values)
{
    Console.WriteLine("Member: {0}",val.ToString());     
}
3Dave
źródło
2

Array ma metodę GetValue (Int32), której można użyć do pobrania wartości o określonym indeksie.

Array.GetValue

Folia bąbelkowa
źródło
2

Możesz to uprościć używając ciągów formatujących. W wiadomościach o użytkowaniu używam następującego fragmentu kodu:

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

Specyfikator formatu D formatuje wartość wyliczenia jako liczbę dziesiętną. Istnieje również specyfikator X, który podaje dane szesnastkowe.

Specyfikator G formatuje wyliczenie jako ciąg. Jeśli atrybut Flags jest stosowany do wyliczenia, obsługiwane są również wartości połączone. Istnieje specyfikator F, który działa tak, jakby flagi były zawsze obecne.

Zobacz Enum.Format ().

Frank Szczerba
źródło
2

W wynikach Enum.GetValues ​​rzutowanie na int daje wartość liczbową. Użycie ToString () daje przyjazną nazwę. Żadne inne wywołania Enum.GetName nie są potrzebne.

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
źródło
0

Oto prosty sposób na iterację po niestandardowym obiekcie Enum

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

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

Next
Chev
źródło
1
Myślę, że OP poprosił o C #.
jm.
0

Starożytne pytanie, ale odpowiedź 3Dave'a zapewniła najłatwiejsze podejście. Potrzebowałem małej metody pomocnika, aby wygenerować skrypt Sql w celu zdekodowania wartości wyliczenia w bazie danych w celu debugowania. Działało świetnie:

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

Mam to w metodzie statycznej, więc użycie jest następujące:

var cheater = MyStaticClass.EnumToCheater<MyEnum>()
Wade Hatler
źródło