Как перебрать все значения Enum в C#: от новичка до профи
Представьте ситуацию: вы пишете игру на C#, и у вас есть перечисление Difficulty с уровнями сложности: Easy, Normal, Hard, Insane. Вам нужно вывести их все в меню выбора, или, скажем, создать выпадающий список для конфигуратора. Как это сделать? Руками писать массив строк? А если завтра добавится новый уровень? Придется лезть в код и менять его в трёх местах.
К счастью, в C# есть элегантное решение. Вопрос "How to loop through all enum values in C#?" — один из самых популярных на Stack Overflow (сотни тысяч просмотров). И сегодня мы разберем его полностью. Вы узнаете не один, а целых 4 способа перебора значений enum, поймете, какой когда использовать, и получите готовый код, который можно скопировать в свой проект.
Поехали!
Что такое Enum в C# и зачем его перебирать?
Enum (перечисление) — это набор именованных констант. Вместо того чтобы помнить, что 0 = Easy, 1 = Normal, 2 = Hard, вы пишете просто Difficulty.Easy. Код становится читаемым и безопасным.
Но когда в проекте 10-20 enum-значений, рано или поздно встает задача: получить их все. Например:
- Заполнить ComboBox или Dropdown в UI.
- Создать тестовые данные для всех вариантов.
- Применить атрибуты к каждому значению.
- Сравнить два enum-набора.
Вместо того чтобы писать:
string[] names = { "Easy", "Normal", "Hard", "Insane" };
…мы используем рефлексию и встроенные методы .NET. Это гибко, масштабируемо и не требует ручного обновления.
Способ 1: Классика — Enum.GetValues (самый популярный)
Это ответ, который вы найдете на Stack Overflow с наибольшим количеством голосов. Метод Enum.GetValues(typeof(YourEnum)) возвращает массив всех значений конкретного перечисления.
Пример кода:
using System;
public enum Difficulty
{
Easy,
Normal,
Hard,
Insane
}
class Program
{
static void Main()
{
Console.WriteLine("Все уровни сложности:");
// Получаем массив значений enum
Difficulty[] allDifficulties = (Difficulty[])Enum.GetValues(typeof(Difficulty));
foreach (Difficulty level in allDifficulties)
{
Console.WriteLine($"Уровень: {level} (числовое значение: {(int)level})");
}
}
}
Вывод в консоль:
Все уровни сложности:
Уровень: Easy (числовое значение: 0)
Уровень: Normal (числовое значение: 1)
Уровень: Hard (числовое значение: 2)
Уровень: Insane (числовое значение: 3)
Важно: Enum.GetValues возвращает массив типа Array, поэтому его нужно явно привести к вашему типу enum. Начиная с .NET 5, появилась дженерик-версия: Enum.GetValues<Difficulty>(), что чуть удобнее.
// .NET 5+
Difficulty[] allDifficulties = Enum.GetValues<Difficulty>();
foreach (var level in allDifficulties)
{
Console.WriteLine(level);
}
Способ 2: Получаем имена через Enum.GetNames
Иногда вам нужны не сами значения, а их строковые имена (например, для UI). Для этого есть Enum.GetNames. Он возвращает массив строк.
Пример:
string[] names = Enum.GetNames(typeof(Difficulty));
Console.WriteLine("Названия уровней:");
foreach (string name in names)
{
Console.WriteLine(name);
}
Вывод:
Названия уровней:
Easy
Normal
Hard
Insane
Когда использовать? Если вам нужны именно строки (например, для отображения в интерфейсе или логирования), а не сами enum-константы.
Способ 3: Linq и преобразование в List<T>
Современный C# и Linq позволяют сделать код еще лаконичнее. Особенно полезно, если вы хотите сразу получить список (List<Difficulty>) или выполнить дополнительные операции (фильтрацию, сортировку).
Пример:
using System;
using System.Linq;
using System.Collections.Generic;
// ...
// Преобразуем в List
List<Difficulty> difficultyList = Enum.GetValues(typeof(Difficulty))
.Cast<Difficulty>()
.ToList();
// Или еще проще (C# 7+)
List<Difficulty> difficultyList2 = Enum.GetValues<Difficulty>().ToList();
Console.WriteLine("Элементов в списке: " + difficultyList.Count);
difficultyList.ForEach(d => Console.WriteLine(d));
Продвинутый пример с фильтрацией: допустим, у вас есть enum с атрибутами, и вы хотите выбрать только те значения, у которых есть определенный атрибут [Description]. Linq отлично с этим справляется.
using System.ComponentModel;
public enum Difficulty
{
[Description("Легко")]
Easy,
[Description("Нормально")]
Normal,
[Description("Сложно")]
Hard,
[Description("Безумие")]
Insane
}
// Получаем только те значения, у которых Description не пустой
var withDescriptions = Enum.GetValues<Difficulty>()
.Where(d =>
{
var field = typeof(Difficulty).GetField(d.ToString());
return Attribute.IsDefined(field, typeof(DescriptionAttribute));
})
.ToList();
Способ 4: Использование Enum.GetValues с кастомными атрибутами
На реальных проектах enum часто содержит кастомные атрибуты — [Display], [Description], [EnumMember]. Чтобы эффективно перебирать такие значения, нужно читать атрибуты внутри цикла.
Полный пример с атрибутом Description:
using System;
using System.ComponentModel;
using System.Reflection;
public enum OperationStatus
{
[Description("Операция выполнена успешно")]
Success,
[Description("Операция не удалась")]
Failed,
[Description("Операция в процессе")]
Pending
}
class Program
{
static void Main()
{
Console.WriteLine("Статусы операций и их описания:");
foreach (OperationStatus status in Enum.GetValues<OperationStatus>())
{
// Получаем поле enum
FieldInfo field = status.GetType().GetField(status.ToString());
// Читаем атрибут Description
DescriptionAttribute attribute = field.GetCustomAttribute<DescriptionAttribute>();
string description = attribute != null ? attribute.Description : status.ToString();
Console.WriteLine($"{status}: {description}");
}
}
}
Вывод:
Статусы операций и их описания:
Success: Операция выполнена успешно
Failed: Операция не удалась
Pending: Операция в процессе
Этот паттерн часто используется в ASP.NET Core для генерации выпадающих списков, в логировании