Напишите пример реализации Singleton на C#
Что проверяют
Знаете ли вы способы реализации и их гарантии потокобезопасности, и понимаете ли, почему в современном коде почти всегда выбирают внедрение зависимостей с временем жизни singleton вместо статического свойства.
Как отвечать
Покажите два рабочих варианта и объясните, кто даёт гарантию:
public sealed class Config
{
public static readonly Config Instance = new Config();
private Config() { }
}
public sealed class LazyConfig
{
private static readonly Lazy<LazyConfig> _lazy = new(() => new LazyConfig());
public static LazyConfig Instance => _lazy.Value;
private LazyConfig() { }
}Класс sealed, конструктор приватный. Потокобезопасность здесь не от вашего кода, а от рантайма: статический конструктор типа выполняется один раз, и повторный вход в него невозможен. Lazy<T> добавляет отложенность и понятнее читается. Двойную проверку с lock стоит упомянуть как учебную классику и сразу сказать, что поле в ней обязано быть volatile. Финал ответа про архитектуру: в приложении на стандартном контейнере это AddSingleton, экземпляр один, а класс остаётся обычным и подменяется в тестах.
Где спотыкаются
Пишут двойную проверку без volatile. Модель памяти платформы допускает переупорядочивание, при котором ссылка публикуется раньше, чем заполнены поля объекта. На x86 это почти не воспроизводится, отсюда и уверенность, что код верный.
Берут блокировку на каждом обращении к свойству. Формально корректно, но синхронизация нужна была один раз при создании, а платить за неё приложение будет всю свою жизнь, причём в самом горячем месте.
Не называют минусов. Синглтон это глобальное состояние и скрытая зависимость: тесты начинают влиять друг на друга, а изменяемые поля внутри требуют собственной синхронизации, про которую вспоминают уже после первой гонки на продакшене.