Как перечислить все значения Enum в C#: полный разбор для новичков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему вопрос "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() незначительна. Однако если вы вызываете эти методы в цикле тысячи раз, стоит закешировать результат:

Блоги

Рекомендаций по книгам