← Назад к вопросам

Что такое аннотация Singleton?

1.0 Junior🔥 251 комментариев
#Dependency Injection и IoC#ООП и паттерны проектирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое аннотация (атрибут) Singleton?

Singleton — это порождающий шаблон проектирования (design pattern), который гарантирует, что у класса есть только один экземпляр, и предоставляет глобальную точку доступа к этому экземпляру. В контексте C# и .NET "аннотацией Singleton" часто называют не язык, а реализацию этого шаблона с использованием атрибутов или других средств, таких как внедрение зависимостей (DI) в ASP.NET Core, либо пользовательские атрибуты для автоматического регистрации сервисов как Singleton. Давайте разберем концепции подробно.

Основная цель Singleton

Шаблон решает две ключевые задачи:

  1. Гарантия единственного экземпляра класса в рамках приложения (или домена).
  2. Предоставление глобального доступа к этому экземпляру.

Это полезно для управления общими ресурсами: кэшем, пулами подключений, конфигурацией, логгерами.

Классическая потокобезопасная реализация в C#

public sealed class Singleton
{
    // Статическая переменная для хранения единственного экземпляра.
    // Использование Lazy<T> гарантирует ленивую инициализацию и потокобезопасность.
    private static readonly Lazy<Singleton> _instance = 
        new Lazy<Singleton>(() => new Singleton());

    // Публичное статическое свойство для доступа к экземпляру.
    public static Singleton Instance => _instance.Value;

    // Приватный конструктор предотвращает создание экземпляров извне.
    private Singleton()
    {
        // Инициализация ресурсов.
    }

    public void SomeBusinessLogic()
    {
        Console.WriteLine("Выполнение бизнес-логики Singleton.");
    }
}

// Использование:
var singleton = Singleton.Instance;
singleton.SomeBusinessLogic();

Singleton как "Аннотация" в ASP.NET Core DI

В современных приложениях на .NET Core/5/6+ чаще используется внедрение зависимостей (Dependency Injection). Контейнер DI позволяет зарегистрировать сервис как Singleton с помощью методов расширения, что можно условно назвать "аннотацией" на уровне конфигурации.

public interface IMyService { void DoWork(); }

public class MyService : IMyService
{
    public void DoWork() => Console.WriteLine("Работа сервиса...");
}

// В Program.cs или Startup.ConfigureServices регистрируем сервис как Singleton:
builder.Services.AddSingleton<IMyService, MyService>();
// ИЛИ для существующего экземпляра:
builder.Services.AddSingleton<IMyService>(new MyService());
// ИЛИ для одного типа:
builder.Services.AddSingleton<MyService>();

Здесь AddSingleton — это метод, который "помечает" сервис как Singleton для контейнера DI. Контейнер будет создавать и предоставлять один и тот же экземпляр на все запросы в течение времени жизни приложения.

Пользовательский атрибут Singleton (как аннотация)

Можно создать собственный атрибут для пометки классов, которые должны быть Singleton. Однако в C# атрибуты сами по себе не изменяют поведение класса — нужен фабричный метод или механизм сканирования сборок.

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class SingletonAttribute : Attribute { }

// Применение атрибута к классу:
[Singleton]
public class DatabaseManager
{
    public void Connect() => Console.WriteLine("Подключено к БД.");
}

// Пример фабрики, использующей рефлексию для создания Singleton:
public static class SingletonFactory
{
    private static readonly Dictionary<Type, object> _instances = new();
    private static readonly object _lock = new();

    public static T GetInstance<T>() where T : class
    {
        var type = typeof(T);
        lock (_lock)
        {
            if (!_instances.TryGetValue(type, out var instance))
            {
                instance = Activator.CreateInstance(type, true); // Вызов приватного конструктора
                _instances[type] = instance;
            }
            return (T)instance;
        }
    }
}

Ключевые моменты и предостережения

  • Глобальное состояние: Singleton вводит глобальное состояние, что усложняет тестирование и нарушает принципы чистой архитектуры. Рекомендуется использовать его осознанно.
  • Потокобезопасность: В классической реализации критически важна защита от многопоточного создания. Lazy<T> — предпочтительный способ.
  • Жизненный цикл: Singleton живёт всё время работы приложения. Это может приводить к утечкам памяти, если он хранит большие данные.
  • Антипаттерн: В некоторых случаях Singleton считается антипаттерном из-за трудностей тестирования (заглушки или моки сложно подставить) и скрытых зависимостей.
  • Альтернативы: Часто лучше использовать внедрение зависимостей, где жизненный циклом объекта управляет контейнер. Это делает код гибче и тестируемее.

Итог

В C# "аннотация Singleton" — это чаще всего:

  1. Шаблон проектирования, реализуемый через приватный конструктор и статическое свойство.
  2. Метод регистрации AddSingleton в контейнере DI ASP.NET Core.
  3. Пользовательский атрибут, требующий дополнительной логики для создания единственного экземпляра.

Для современной разработки предпочтительнее использовать встроенный контейнер DI, который управляет жизненным циклом объектов, обеспечивая чистоту кода и упрощая модульное тестирование.