Почему вопрос "How can I enumerate an enum?" так популярен?
Если вы когда-нибудь писали на C# и сталкивались с перечислениями (enum), то наверняка задавались вопросом: «А как мне получить все значения этого enum?». Этот вопрос — настоящий хит на Stack Overflow, набравший тысячи просмотров. И неудивительно: перечисление enum — одна из тех задач, которая кажется простой, но без знания правильного синтаксиса может поставить в тупик даже опытного разработчика.
В этой статье мы разберём, как правильно перечислить все значения enum в C#, покажем рабочие примеры кода, которые можно сразу скопировать в свой проект, и расскажем о подводных камнях. Поехали!
Что такое enum в C#? Краткое напоминание
Enum (перечисление) — это тип данных, который позволяет задать набор именованных констант. Например, дни недели, статусы заказа или типы ошибок. В C# enum объявляется так:
public enum DayOfWeek
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
Каждому элементу по умолчанию присваивается целочисленное значение (начиная с 0). Но часто возникает необходимость перебрать все элементы enum — например, чтобы вывести их в выпадающий список, создать массив или проверить, существует ли значение.
Как перечислить все значения enum: базовый способ
Самый популярный и простой способ — использовать статический метод Enum.GetValues(). Он возвращает массив всех значений указанного перечисления. Вот как это работает:
using System;
public class Program
{
public enum Status
{
New,
InProgress,
Completed,
Cancelled
}
public static void Main()
{
// Получаем все значения enum
Status[] allStatuses = (Status[])Enum.GetValues(typeof(Status));
// Перебираем их с помощью foreach
foreach (Status status in allStatuses)
{
Console.WriteLine(status);
}
}
}
Результат выполнения:
New
InProgress
Completed
Cancelled
Обратите внимание: мы явно приводим результат к массиву Status[], потому что Enum.GetValues() возвращает Array (нетипизированный).
Альтернатива: Enum.GetNames()
Если вам нужны не сами значения, а только их строковые имена, используйте Enum.GetNames():
string[] statusNames = Enum.GetNames(typeof(Status));
foreach (string name in statusNames)
{
Console.WriteLine(name);
}
Результат будет таким же, но тип элемента — string, а не Status. Это удобно, когда вы работаете с UI или сериализацией.
Современный подход: generic версия (C# 7.3+)
Начиная с C# 7.3, у нас есть более элегантный способ — Enum.GetValues<T>(). Он возвращает массив конкретного типа, и приведение не требуется:
Status[] allStatuses = Enum.GetValues<Status>();
foreach (Status status in allStatuses)
{
Console.WriteLine($"ID: {(int)status}, Name: {status}");
}
Этот метод короче и безопаснее с точки зрения типов. Если вы используете современную версию C# (а вы должны!), рекомендую именно его.
Пример из реальной жизни: заполнение выпадающего списка
Представьте, что вы пишете приложение для управления задачами. Вам нужно показать пользователю список возможных статусов в выпадающем меню. Вот как это делается с помощью перечисления enum:
using System;
using System.Collections.Generic;
public class TaskManager
{
public enum TaskStatus
{
NotStarted,
InProgress,
WaitingForReview,
Done
}
public static List<string> GetStatusList()
{
List<string> statuses = new List<string>();
foreach (TaskStatus status in Enum.GetValues<TaskStatus>())
{
statuses.Add($"{status} ({(int)status})");
}
return statuses;
}
public static void Main()
{
var list = GetStatusList();
Console.WriteLine("Выберите статус:");
foreach (var item in list)
{
Console.WriteLine($"- {item}");
}
}
}
Вывод:
Выберите статус:
- NotStarted (0)
- InProgress (1)
- WaitingForReview (2)
- Done (3)
Такой подход часто используется в WinForms, WPF или ASP.NET для заполнения ComboBox, DropDownList или SelectListItem.
Подводные камни: что нужно знать новичку
1. Enum может содержать повторяющиеся значения
Технически вы можете задать два элемента с одинаковым числовым значением. Enum.GetValues() вернёт оба, но порядок не гарантирован:
enum Colors
{
Red = 1,
Blue = 1, // то же значение!
Green = 2
}
// Enum.GetValues() вернёт Red, Blue, Green (или Red, Green, Blue — зависит от реализации)
Будьте внимательны: если вы используете Enum.GetValues() для проверки уникальности, вы можете получить дубликаты.
2. Порядок элементов не гарантирован
Документация Microsoft не обещает, что Enum.GetValues() вернёт элементы в том же порядке, в котором они объявлены. На практике порядок обычно совпадает с порядком объявления, но полагаться на это не стоит. Если важен порядок — сортируйте вручную.
3. Enum с атрибутом [Flags]
Если ваш enum помечен атрибутом [Flags], Enum.GetValues() вернёт только отдельные значения, но не все возможные комбинации. Для работы с флагами нужен другой подход (например, рекурсивный перебор).
Как проверить, существует ли значение в enum?
Часто нужно не перебрать все значения, а проверить, есть ли конкретное значение в перечислении. Для этого существует метод Enum.IsDefined():
enum Priority
{
Low,
Medium,
High
}
bool exists = Enum.IsDefined(typeof(Priority), "Medium"); // true
bool existsNumber = Enum.IsDefined(typeof(Priority), 2); // false (High имеет значение 2? Нет, Low=0, Medium=1, High=2)
Но будьте осторожны: Enum.IsDefined() не работает с комбинациями флагов и может давать ложные срабатывания для чисел, которые не соответствуют ни одному элементу, если enum имеет базовый тип int.
Современная альтернатива — использовать Enum.TryParse():
if (Enum.TryParse<Priority>("High", out Priority result))
{
Console.WriteLine($"Значение {result} существует");
}
else
{
Console.WriteLine("Такого значения нет");
}
Производительность: что быстрее?
Для большинства приложений разница в скорости между Enum.GetValues() и Enum.GetNames() незначительна. Однако если вы вызываете эти методы в цикле тысячи раз, стоит закешировать результат: