Singleton на C#: когда применять и как реализовать

Паттерн Singleton (одиночка) гарантирует существование ровно одного экземпляра класса и предоставляет глобальную точку доступа к нему. В разделе C# тот же принцип «один экземпляр на приложение» часто реализуют через DI, а не через ручной статический класс — см. ASP.NET.


Где Singleton приносит пользу

  • централизованный конфиг приложения;
  • кэш в рамках процесса;
  • клиент к редкому внешнему ресурсу;
  • сервис, который обязан иметь единое состояние.

Ключевые требования

  1. закрытый конструктор;
  2. единственный экземпляр внутри класса;
  3. публичный доступ к экземпляру;
  4. корректная работа в многопоточном окружении.

Базовая безопасная реализация в C#

Почему этот вариант надежный:

  • Lazy<T> обеспечивает потокобезопасную ленивую инициализацию;
  • sealed закрывает наследование и упрощает модель объекта;
  • конструктор недоступен снаружи.

Пошаговый сценарий внедрения

Шаг 1. Определите, нужен ли единый экземпляр

Проверьте:

  • объект хранит общее состояние;
  • дублирование экземпляров создает риск рассинхрона;
  • доступ требуется из нескольких модулей.

Шаг 2. Реализуйте класс по шаблону выше

Начните с минимального интерфейса и избегайте лишних обязанностей.


Шаг 3. Подключите в коде

var env = AppConfig.Instance.EnvironmentName;
AppConfig.Instance.SetEnvironment("Production");

Шаг 4. Добавьте тесты поведения

Проверьте:

  1. две ссылки указывают на один объект;
  2. изменение состояния через одну ссылку видно через другую;
  3. параллельный доступ не создает второй экземпляр.

Частые ошибки

  • ленивый singleton без потокобезопасности;
  • хранение бизнес-логики внутри singleton-класса;
  • сильная связность всего приложения через глобальный объект;
  • использование Singleton там, где подходит DI-контейнер.

Когда выбрать другой подход

В современных приложениях на ASP.NET Core чаще используют внедрение зависимостей и управляют временем жизни сервисов через контейнер:

  • AddSingleton — один экземпляр на приложение (аналог паттерна в DI);
  • AddScoped — один экземпляр на HTTP-запрос;
  • AddTransient — новый экземпляр при каждом запросе.

Подробнее о времени жизни — в ASP.NET — фреймворк и Web API.

Если проект уже на DI, регистрируйте сервис как singleton в Program.cs:

builder.Services.AddSingleton<IAppConfig, AppConfigService>();

Практическое задание

  1. Реализуйте singleton AppSettingsStore.
  2. Добавьте методы чтения и обновления одного параметра.
  3. Напишите тесты на идентичность экземпляра и потокобезопасность.
  4. Подготовьте короткий вывод: почему singleton подходит или не подходит для вашей задачи.

Рекомендую читать в энциклопедии

Тема Материал
Маршрут по C# C# — о разделе
Классы, наследование, инкапсуляция ООП в C#
Паттерн Singleton в теории Порождающие паттерны
Обзор паттернов Паттерны проектирования — о разделе
DI и AddSingleton в ASP.NET ASP.NET — фреймворк
Регистрация сервисов в Web API ASP.NET Web API