Ответ
Singleton — это порождающий паттерн проектирования, который гарантирует, что у класса существует только один экземпляр, и предоставляет к нему глобальную точку доступа.
Зачем это нужно? Для управления общим ресурсом, доступ к которому должен быть координирован из одной точки: кэш, конфигурация, логгер, пул соединений с базой данных.
Классическая потокобезопасная реализация на C# (с двойной проверкой):
public sealed class Singleton
{
// Единственный экземпляр. 'volatile' гарантирует корректную работу в многопоточной среде.
private static volatile Singleton _instance;
private static readonly object _lockObject = new object();
// Приватный конструктор блокирует создание экземпляров через 'new'.
private Singleton() { }
// Публичное статическое свойство для доступа.
public static Singleton Instance
{
get
{
// Первая проверка (для быстродействия, если экземпляр уже создан).
if (_instance == null)
{
lock (_lockObject) // Блокировка для одного потока.
{
// Вторая проверка внутри блокировки.
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
}
Использование:
var config = Singleton.Instance;
Современный и предпочтительный способ в .NET — использование Lazy<T>:
public sealed class Singleton
{
// Lazy<T> обеспечивает ленивую инициализацию и потокобезопасность по умолчанию.
private static readonly Lazy<Singleton> _lazyInstance =
new Lazy<Singleton>(() => new Singleton());
private Singleton() { }
public static Singleton Instance => _lazyInstance.Value;
}
Важно: Паттерн может считаться антипаттерном, если приводит к излишнему глобальному состоянию и усложняет тестирование. В таких случаях лучше использовать внедрение зависимостей (DI), где жизненный цикл объекта (например, AddSingleton) управляется контейнером.
Ответ 18+ 🔞
А, синглтон, блядь! Ну это ж классика, ёпта, как "Муму" у Тургенева — все знают, а половина нихуя не понимает, зачем оно надо.
Смотри, представь себе, что у тебя есть, ну, например, главный настройщик всего приложения. Конфигурация, блядь. И вот ты в каждой комнате своего кода кричишь: "Эй, а какую базу данных использовать-то?". И если у тебя таких настройщиков будет несколько, как этих Толстых, блядь, — один Лев, другой Алексей, — то они начнут говорить разное. Один скажет "localhost", а другой — "какая-то левая амазонка, нахуй". И всё, пиздец, приложение ебнется.
Вот чтобы этого не было, придумали синглтон — это такой единственный царь-батюшка для своего класса. Создали его один раз, и все ходят к нему на поклон за данными. Больше экземпляров — ни-ни. Как Герасим, только один на всю деревню.
Как это делается по-старинке, с потрохами и блокировками:
public sealed class Singleton // 'sealed' — чтобы наследников, блядь, не было, а то начнут плодиться
{
// Вот он, наш царь в ожидании. volatile — чтоб потоки его не ебали с кэшами процессора.
private static volatile Singleton _instance;
// Объект для блокировки. Без него в многопоточке будет пиздец — создадут сто царей.
private static readonly object _lockObject = new object();
// Конструктор приватный! Это чтобы какой-нибудь умник не сделал 'new Singleton()'. Хуй тебе, а не new.
private Singleton() { }
// А вот это — главные ворота во дворец.
public static Singleton Instance
{
get
{
// Первая быстрая проверка: а может, царь уже есть? Тогда не лезем в блокировку.
if (_instance == null)
{
lock (_lockObject) // Здесь очередь. Заходит один поток, остальные ждут, как в сортире.
{
// Вторая проверка уже внутри. А вдруг пока этот поток ждал, царя уже создали?
if (_instance == null)
{
_instance = new Singleton(); // Коронация, блядь!
}
}
}
return _instance; // И вот он, наш повелитель.
}
}
}
Использовать — проще некуда:
var config = Singleton.Instance; // Все идут в одну дверь.
Но это, блядь, как на Западе — всё с блокировками, с проверками. Нам, русским программистам, такое не очень. Много писанины. Поэтому в .NET есть способ попизже — Lazy<T>. Это как ленивый мужик на печи: он тебе ничего делать не будет, пока ты его за шкирку не дёрнешь.
Современный способ, который все сейчас используют:
public sealed class Singleton
{
// Lazy<T> — он сам всё сделает: и ленивую инициализацию, и потокобезопасность. Умная хуйня.
private static readonly Lazy<Singleton> _lazyInstance =
new Lazy<Singleton>(() => new Singleton());
private Singleton() { } // Приватный конструктор — святое!
// Только когда кто-то попросит Value — тогда он и создастся. Красота!
public static Singleton Instance => _lazyInstance.Value;
}
Вот и вся магия. Но, предупреждаю, как и всякая глобальная хуйня, синглтон — палка о двух концах. Если ты его начнёшь таскать по всему коду, как ту самую Муму, то в итоге всё так переплетётся, что тестировать будет невозможно. Современные ребята из-за этого вообще против него, говорят — антипаттерн. Вместо этого они используют внедрение зависимостей (DI), где контейнер сам решает, что у тебя будет один экземпляр на все запросы (AddSingleton). Это, конечно, цивилизованнее, но чтобы понять DI, нужно сначала с синглтоном разобраться, а то будешь как Герасим — мычать и ничего не понимать.
Короче, инструмент как инструмент. Главное — не выеби себе им мозг.