Что такое синглтон в C #?

182

Что такое синглтон и когда я должен его использовать?

Серхио Тапиа
источник
2
Возможный дубликат: stackoverflow.com/questions/246710/…
Марк Симанн
4
Кроме того, Singleton является одним из наиболее широко используемых и злоупотребляемых шаблонов проектирования в ОО-программировании.
ChaosPandion
3
@Fabiano: Потому что у него есть способ создания связей, которые не имеют смысла (как я могу Xпоговорить Y? Просто сделайте Yсинглтон!), Что , в свою очередь, приводит к трудностям тестирования / отладки и процедурному стилю программирования. Иногда синглтоны необходимы; большую часть времени, нет.
Aaronaught
3
Это один из моих стандартных вопросов по телефону. Правильный ответ: никогда.
Джонни
3
@jonnii это хорошо, это помогает предупредить потенциальных разработчиков о том, на что похож босс!
Мистер Бой

Ответы:

145

Singleton - это класс, который позволяет создавать только один его экземпляр и обеспечивает простой и легкий доступ к нему. Одиночная предпосылка - образец всей разработки программного обеспечения.

Существует реализация C # «Реализация шаблона Singleton в C #», охватывающая большую часть того, что вам нужно знать, включая некоторые полезные советы относительно безопасности потоков .

Честно говоря, очень редко нужно реализовывать синглтон - по моему мнению, это должно быть одной из тех вещей, о которых вы должны знать, даже если она не используется слишком часто.

Даниэль Мэй
источник
2
хороший учебник, но,
черт возьми,
Вот более прямая ссылка на то, что я считаю идеальной реализацией в 2020 году. То есть « использование типа Lazy <T> в .NET 4 », а также ссылку на документ Microsoft Doc для Lazy<T> Class.
Тирамису
52

Вы просили C #. Тривиальный пример:


public class Singleton
{
    private Singleton()
    {
        // Prevent outside instantiation
    }

    private static readonly Singleton _singleton = new Singleton();

    public static Singleton GetSingleton()
    {
        return _singleton;
    }
}
Крис Симмонс
источник
14
not thread safe. Два потока могут вызывать одновременно и могут создавать два отдельных объекта.
Alagesan Palani
5
@Alagesan Palani, действительно, ты прав. Я не очень разбираюсь в низкоуровневых деталях инициализации на уровне класса, но я думаю, что внесенные мною изменения направлены на обеспечение безопасности потоков.
Крис Симмонс
3
конечно, я НЕ указываю, что вы не правы. я даю подсказку читателю о безопасности потоков, чтобы они были осторожны, если им придется иметь дело с этим.
Alagesan Palani
9
Нет, я думаю, что ваш комментарий важен. Учитывая, что синглтон должен доставить один - и только один - экземпляр, состояние гонки здесь открывает возможность доставки более одного. Смотрите версию сейчас, со статической инициализацией поля. Я полагаю, что это устраняет проблему безопасности потока, если я правильно прочитал документы и этот SO ответ .
Крис Симмонс
1
@AlagesanPalani, я вижу, что вы заявили, что некоторые другие ответы не являются потокобезопасными. Вы хотели бы предложить решение, которое является поточно-ориентированным?
Bonez024
39

Что это: класс, для которого существует только один постоянный экземпляр на протяжении всего жизненного цикла приложения. См. Шаблон Singleton .

Когда вы должны использовать это: как можно меньше. Только тогда, когда вы абсолютно уверены, что вам это нужно. Я не хочу говорить «никогда», но обычно есть лучшая альтернатива, например, Dependency Injection или просто статический класс.

Aaronaught
источник
16
Я не уверен, что статический класс - лучшая альтернатива, чем синглтон ... это действительно зависит от ситуации и языка.
Marcgg
5
Статические классы не ведут себя так же, как синглтон, синглтон может быть передан в методы как параметр, тогда как статический класс не может.
TabbyCool
4
Согласен с marcgg - я не считаю статический класс хорошей альтернативой синглетам, потому что у вас все еще есть проблема с заменой, например, во время тестирования компонента, который зависит от этого класса. Но я также вижу различные варианты использования: статический класс обычно используется для независимых служебных функций, которые не зависят от состояния, где синглтон является фактическим экземпляром класса и обычно хранит состояние. Я полностью согласен использовать DI вместо этого, а затем сказать вашему контейнеру DI, что вы хотите, чтобы он использовал только один экземпляр этого класса.
Пит
9
Я отклонил этот ответ, потому что он не дает мне никакой информации о том, когда его использовать. «Только когда вам это нужно» вообще не дает мне никакой информации для кого-то, кто плохо знаком с синглетонами.
Серхио Тапия
9
@Adkins: DI означает внедрение зависимостей, когда любые зависимости класса передаются через (обычно) конструктор или открытое свойство. Один только DI не решает проблему «расстояния», но обычно он реализуется вместе с контейнером Inover-of-Control (IoC), который знает, как автоматически инициализировать любые зависимости. Поэтому, если вы создаете Singleton для решения проблемы «X не знает, как найти / поговорить с Y», комбинация DI и IoC может решить ту же проблему с более слабой связью.
Aaronaught
27

Другой способ реализовать синглтон в C #, я лично предпочитаю этот способ, потому что вы можете получить доступ к экземпляру класса Singeton как свойство вместо метода.

public class Singleton
    {
        private static Singleton instance;

        private Singleton() { }

        public static Singleton Instance
        {
            get
            {
                if (instance == null)
                    instance = new Singleton();
                return instance;
            }
        }

        //instance methods
    }

но, насколько я знаю, оба способа считаются «правильными», так что это просто вещь личного вкуса.

Марникс против Р.
источник
11
not thread safe. Два потока могут вызывать одновременно и могут создавать два отдельных объекта.
Alagesan Palani
11
using System;
using System.Collections.Generic;
class MainApp
{
    static void Main()
    {
        LoadBalancer oldbalancer = null;
        for (int i = 0; i < 15; i++)
        {
            LoadBalancer balancerNew = LoadBalancer.GetLoadBalancer();

            if (oldbalancer == balancerNew && oldbalancer != null)
            {
                Console.WriteLine("{0} SameInstance {1}", oldbalancer.Server, balancerNew.Server);
            }
            oldbalancer = balancerNew;
        }
        Console.ReadKey();
    }
}

class LoadBalancer
{
    private static LoadBalancer _instance;
    private List<string> _servers = new List<string>();
    private Random _random = new Random();

    private static object syncLock = new object();

    private LoadBalancer()
    {
        _servers.Add("ServerI");
        _servers.Add("ServerII");
        _servers.Add("ServerIII");
        _servers.Add("ServerIV");
        _servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
        if (_instance == null)
        {
            lock (syncLock)
            {
                if (_instance == null)
                {
                    _instance = new LoadBalancer();
                }
            }
        }

        return _instance;
    }

    public string Server
    {
        get
        {
            int r = _random.Next(_servers.Count);
            return _servers[r].ToString();
        }
    }
}

Я взял код с dofactory.com , ничего особенного, но я нахожу это гораздо лучше, чем примеры с Foo and Bar, дополнительно в книге от Джудит Бишоп о C # 3.0 Design Patterns есть пример активного приложения в доке Mac.

Если вы посмотрите на код, мы фактически строим новые объекты для цикла for , так что он создает новый объект, но повторно использует экземпляр, в результате чего oldbalancer и newbalancer имеют один и тот же экземпляр. Как? Это происходит из-за того, что ключевое слово static используется в функции GetLoadBalancer () , несмотря на то, что сервер имеет другое значение, которое является случайным списком, static в GetLoadBalancer () принадлежит самому типу, а не конкретному объекту.

Кроме того , существует двойная проверка блокировки здесь

if (_instance == null)
            {
                lock (syncLock)
                {
                    if (_instance == null)

так как из MSDN

Ключевое слово lock гарантирует, что один поток не войдет в критический раздел кода, в то время как другой поток находится в критическом разделе. Если другой поток попытается ввести заблокированный код, он будет ждать блокировки до тех пор, пока объект не будет освобожден.

поэтому каждый раз выдается блокировка взаимного исключения, даже если она не нужна, что является ненужным, поэтому у нас есть нулевая проверка.

Надеюсь, это поможет очистить больше.

И, пожалуйста, прокомментируйте, если я понимаю, что неверно.

Сандип Бантава
источник
6

Синглтон (и это не связано с C #, это шаблон проектирования ОО) - это когда вы хотите, чтобы в вашем приложении создавался только ОДИН экземпляр класса. Использование обычно включает в себя глобальные ресурсы, хотя я скажу из личного опыта, они очень часто являются источником большой боли.

BFree
источник
5

Хотя может быть только один экземпляр синглтона, это не то же самое, что статический класс. Статический класс может содержать только статические методы и никогда не может быть создан, в то время как экземпляр синглтона может использоваться так же, как и любой другой объект.

TabbyCool
источник
2

Это шаблон дизайна, и он не специфичен для c #. Подробнее об этом по всему Интернету и ТАК, как в этой статье в Википедии .

В разработке программного обеспечения одноэлементный шаблон является шаблоном проектирования, который используется для ограничения создания экземпляров класса одним объектом. Это полезно, когда ровно один объект необходим для координации действий в системе. Эта концепция иногда обобщается на системы, которые работают более эффективно, когда существует только один объект, или которые ограничивают создание экземпляра определенным числом объектов (скажем, пять). Некоторые считают это анти-паттерном, полагая, что он чрезмерно используется, вводит ненужные ограничения в ситуациях, когда единственный экземпляр класса фактически не требуется, и вводит глобальное состояние в приложение.

Вы должны использовать его, если вы хотите класс, который может быть создан только один раз.

marcgg
источник
2

Я использую это для поиска данных. Загрузите один раз из БД.

public sealed class APILookup
    {
        private static readonly APILookup _instance = new APILookup();
        private Dictionary<string, int> _lookup;

        private APILookup()
        {
            try
            {
                _lookup = Utility.GetLookup();
            }
            catch { }
        }

        static APILookup()
        {            
        }

        public static APILookup Instance
        {
            get
            {
                return _instance;
            }
        }
        public Dictionary<string, int> GetLookup()
        {
            return _lookup;
        }

    }
Маниш джайн
источник
2

Что такое синглтон?
Это класс, который позволяет создавать только один его экземпляр и обычно предоставляет простой доступ к этому экземпляру.

Когда следует использовать:
это зависит от ситуации.

Примечание: пожалуйста, не используйте соединение с БД, подробный ответ см. В ответе @Chad Grant.

Вот простой пример Singleton:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

Вы также можете использовать, Lazy<T>чтобы создать свой Singleton.

Смотрите здесь для более подробного примера использованияLazy<T>

MSTdev
источник
1

Вот что такое синглтон: http://en.wikipedia.org/wiki/Singleton_pattern

Я не знаю C #, но на самом деле это одно и то же во всех языках, отличается только реализация.

Обычно вам следует избегать синглтона, когда это возможно, но в некоторых ситуациях это очень удобно.

Извините за мой английский ;)

Radex
источник
ваш английский в порядке :)
FrenkyB
1

Класс Singleton используется для создания единого экземпляра для всего домена приложения.

public class Singleton
{
    private static Singleton singletonInstance = CreateSingleton();

    private Singleton()
    {
    }

    private static Singleton CreateSingleton()
    {
        if (singletonInstance == null)
        {
            singletonInstance = new Singleton();
        }

        return singletonInstance;
    }

    public static Singleton Instance
    {
        get { return singletonInstance; }            
    }
}

В этой статье описывается, как мы можем создать потокобезопасный одноэлементный класс с использованием переменной readonly и их практическое использование в приложениях.

Викрам
источник
1

Я знаю, что уже очень поздно, чтобы ответить на вопрос, но с Auto-Property вы можете сделать что-то вроде этого:

public static Singleton Instance { get; } = new Singleton();

Где Singletonвы класс и может быть через, в этом случае свойство только для чтения Instance.

Zyo
источник
0

EX Вы можете использовать Singleton для глобальной информации, которую необходимо ввести.

В моем случае я хранил данные о зарегистрированном пользователе (имя пользователя, разрешения и т. Д.) В глобальном статическом классе. И когда я попытался реализовать модульный тест, я никак не мог внедрить зависимость в классы контроллера. Таким образом, я изменил свой статический класс на шаблон Singleton.

public class SysManager
{
    private static readonly SysManager_instance = new SysManager();

    static SysManager() {}

    private SysManager(){}

    public static SysManager Instance
    {
        get {return _instance;}
    }
}

http://csharpindepth.com/Articles/General/Singleton.aspx#cctor

Gubi
источник
0

Нам нужно использовать шаблон проектирования Singleton в C #, когда нам нужно убедиться, что будет создан только один экземпляр определенного класса, а затем обеспечить простой глобальный доступ к этому экземпляру для всего приложения.

Сценарии реального времени, в которых вы можете использовать шаблон проектирования Singleton: прокси-серверы службы. Как мы знаем, вызов API-интерфейса службы - это обширная операция в приложении. Процесс, который занимает большую часть времени, создает клиента службы для вызова API службы. Если вы создадите прокси-сервер службы в качестве Singleton, это улучшит производительность вашего приложения.

Фасады: Вы также можете создать подключения к базе данных как Singleton, что может повысить производительность приложения.

Журналы: в приложении выполнение операции ввода-вывода над файлом является дорогостоящей операцией. Если вы создадите Logger как Singleton, это улучшит производительность операции ввода-вывода.

Совместное использование данных: если у вас есть какие-либо постоянные значения или значения конфигурации, вы можете сохранить эти значения в Singleton, чтобы их могли прочитать другие компоненты приложения.

Кэширование. Как мы знаем, выборка данных из базы данных занимает много времени. В вашем приложении вы можете кэшировать мастер и конфигурацию в памяти, что позволит избежать вызовов БД. В таких ситуациях класс Singleton может использоваться для эффективной обработки кэширования с синхронизацией потоков, что значительно повышает производительность приложения.

Недостатки шаблона проектирования Singleton в C # Недостатки использования шаблона проектирования Singleton в C # заключаются в следующем:

Модульное тестирование очень сложно, потому что оно вводит глобальное состояние в приложение. Это снижает вероятность параллелизма внутри программы, поскольку для доступа к одноэлементному экземпляру в многопоточной среде необходимо сериализовать объект с помощью блокировки.

Я взял это из следующей статьи.

https://dotnettutorials.net/lesson/singleton-design-pattern/

Праная Путь
источник
0

Поток Safe Singleton без использования замков и без ленивых экземпляров.

Эта реализация имеет статический конструктор, поэтому она выполняется только один раз для каждого домена приложения.

public sealed class Singleton
{

    static Singleton(){}

    private Singleton(){}

    public static Singleton Instance { get; } = new Singleton();

}
grigb
источник