Что такое конечный автомат (State Machine)?

«Что такое конечный автомат (State Machine)?» — вопрос из категории Паттерны, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Конечный автомат (State Machine, FSM) — это поведенческий паттерн, который позволяет объекту изменять своё поведение в зависимости от внутреннего состояния. Он формализует переходы между конечным числом состояний в ответ на события.

Основные компоненты:

  1. Состояния (States): Определённые конфигурации системы (например, Idle, Processing, Completed).
  2. Переходы (Transitions): Правила, определяющие, как и при каких событиях происходит смена состояния.
  3. События (Events): Входные сигналы или действия, инициирующие переходы.

Практический пример: Заказ в интернет-магазине

public enum OrderState { New, Paid, Shipped, Delivered, Cancelled }

public class Order
{
    public OrderState CurrentState { get; private set; } = OrderState.New;
    private Dictionary<(OrderState, string), OrderState> _transitions;

    public Order()
    {
        // Определяем правила переходов: (Текущее состояние, Событие) -> Новое состояние
        _transitions = new Dictionary<(OrderState, string), OrderState>
        {
            { (OrderState.New, "Pay"), OrderState.Paid },
            { (OrderState.Paid, "Ship"), OrderState.Shipped },
            { (OrderState.Shipped, "Deliver"), OrderState.Delivered },
            { (OrderState.New, "Cancel"), OrderState.Cancelled },
            { (OrderState.Paid, "Cancel"), OrderState.Cancelled }
        };
    }

    public bool TryProcessEvent(string eventName)
    {
        var key = (CurrentState, eventName);
        if (_transitions.TryGetValue(key, out var newState))
        {
            CurrentState = newState;
            Console.WriteLine($"Order state changed to {CurrentState} after event '{eventName}'");
            return true;
        }
        Console.WriteLine($"Invalid transition from {CurrentState} on event '{eventName}'");
        return false;
    }
}

// Использование
var order = new Order();
order.TryProcessEvent("Pay");     // OK: New -> Paid
order.TryProcessEvent("Ship");    // OK: Paid -> Shipped
order.TryProcessEvent("Pay");     // Ошибка: Нет перехода из Shipped по событию Pay

Зачем это нужно?

  • Упрощение сложной логики: Вместо множества if-else или switch по флагам используется чётко определённая таблица переходов.
  • Предсказуемость: Все возможные состояния и переходы явно описаны.
  • Лёгкость тестирования: Можно протестировать каждый переход независимо.
  • Соблюдение инвариантов: Невозможны недопустимые переходы (например, отменить уже доставленный заказ).

State Machine и async/await в .NET:

Компилятор C# преобразует асинхронные методы (async/await) в машину состояний (State Machine). Это структура, реализующая интерфейс IAsyncStateMachine, которая:

  • Хранит текущую позицию выполнения метода (в виде целого числа state).
  • «Поднимает» локальные переменные в поля структуры, чтобы их значения сохранялись между await.
  • Управляет продолжением выполнения после завершения асинхронной операции.

Почему так сделано? Это позволяет разбить метод на логические шаги, приостанавливать выполнение без блокировки потока и возобновлять его с нужного места, что и создаёт иллюзию последовательного асинхронного кода.