C # Итерации по перечислению? (Индексирование System.Array)

113

У меня такой код:

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

Однако я не могу индексировать значения. Есть ли более простой способ сделать это?

Или я что-то совсем упустил!

ТЗ.
источник

Ответы:

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

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

Или вы можете привести возвращаемый 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]);
}

Но можете ли вы быть уверены, что GetValues ​​возвращает значения в том же порядке, в котором GetNames возвращает имена?

Фредерик Гейзель
источник
3
«Но можете ли вы быть уверены, что GetValues ​​возвращает значения в том же порядке, в каком GetNames возвращает имена?» - Это очень хороший момент, и я еще не затронул его! Я думаю, что ваше первое решение, вероятно, предоставит способ надежного сопоставления значений и строк
TK.
Здравствуйте, я уже встречал упоминания об этом подозрении на "несоответствие индекса", возникающем при этом; однако мне еще предстоит выяснить, действительно ли это вызывает беспокойство? Существуют ли какие-либо определенные случаи, когда это предположение может ошибаться? Спасибо!
Funka
Вы, вероятно, захотите преобразовать второй "val" в int, если вы хотите устранить несоответствие значений, как Enum.GetName(typeof(MyEnum), val), (int)val)в том случае, когда в выходных данных указывается имя и номер перечисления.
Грег
GetValues ​​и GetNames возвращаются в том же порядке, а именно: «Элементы массива возвращаемых значений сортируются по двоичным значениям перечисленных констант (то есть по их беззнаковой величине)».
Рассел Борогов,
Я считаю, что вы также можете использовать LINQ напрямую, вызвав Cast<T>результат:Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>()...
jocull
33

Вам необходимо преобразовать массив - возвращаемый массив действительно имеет запрошенный тип, т.е. myEnum[]если вы запрашиваете typeof(myEnum):

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

Потом и values[0]т. Д.

Марк Гравелл
источник
9

Вы можете преобразовать этот массив в различные типы массивов:

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

или если вам нужны целочисленные значения:

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

Конечно, вы можете перебирать эти приведенные массивы :)

Арктур
источник
7

Как насчет списка словарей?

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

и, конечно, вы можете изменить тип значения словаря на любые значения вашего перечисления.

mrtaikandi
источник
Я думаю, что это было бы правильным решением, но, к сожалению, перечисление находится в области кода, которую я не могу контролировать!
ТЗ.
1
Единственное найденное мной решение, которое получает фактические целочисленные значения перечисления!
SharpC 03
5

Другое решение с интересными возможностями:

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;
  } 
}
Павел
источник
4

Вот еще один. Нам нужно было предоставить понятные имена для наших EnumValues. Мы использовали атрибут System.ComponentModel.DescriptionAttribute, чтобы показать настраиваемое строковое значение для каждого значения перечисления.

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

В использовании

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



    static void Main(string[] args)
    {

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

Это закончится возвратом "Something 1", "Something 2".

Кевин Кастл
источник
1
Хорошо только в том случае, если описания статичны ... если ваши описания необходимо изменить на основе экземпляра, этот подход не будет работать.
Llyle 01
3

Как насчет использования цикла foreach, может быть, вы могли бы с этим поработать?

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

что-то подобное возможно?

TWith2Sugars
источник
Я знаю об этом ... но мне нужно получить доступ как к именам, так и к значениям в «синхронизации» ... скажем, имена [2] связаны со значениями [2], и я не уверен, как этого добиться в цикл foreach!
ТЗ.
Я добавил пример - посмотрим, поможет ли это.
TWith2Sugars
3

Старый вопрос, но немного более чистый подход с использованием LINQ .Cast<>()

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

foreach(var val in values)
{
    Console.WriteLine("Member: {0}",val.ToString());     
}
3Dave
источник
2

В массиве есть метод GetValue (Int32), который можно использовать для получения значения по указанному индексу.

Array.GetValue

Bubblewrap
источник
2

Вы можете упростить это, используя строки формата. Я использую следующий фрагмент в сообщениях об использовании:

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

Спецификатор формата D форматирует значение перечисления как десятичное. Также есть спецификатор X, который дает шестнадцатеричный вывод.

Спецификатор G форматирует перечисление как строку. Если к перечислению применяется атрибут Flags, то также поддерживаются комбинированные значения. Существует спецификатор F, который действует так, как будто флаги всегда присутствуют.

См. Enum.Format ().

Франк Щерба
источник
2

В результатах Enum.GetValues ​​приведение к int дает числовое значение. Использование ToString () дает понятное имя. Никаких других вызовов 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
 */
Майк Водарчик
источник
0

Вот простой способ перебрать ваш пользовательский объект Enum

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

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

Next
Чев
источник
1
Я думаю, OP попросил C #.
jm.
0

Древний вопрос, но ответ 3Dave предоставил самый простой подход. Мне нужен был небольшой вспомогательный метод для генерации сценария Sql для декодирования значения перечисления в базе данных для отладки. Это сработало отлично:

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

У меня это статический метод, поэтому использование:

var cheater = MyStaticClass.EnumToCheater<MyEnum>()
Уэйд Хатлер
источник