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


181

Що таке Singleton і коли я повинен його використовувати?


2
Можливий дублікат: stackoverflow.com/questions/246710/…
Марк Семанн

4
Також Singleton є однією з найбільш широко використовуваних та зловживаних моделей дизайну в програмуванні OO.
ChaosPandion

3
@Fabiano: Оскільки у нього є спосіб створення муфт, який не має сенсу (як я можу Xпоговорити Y? Просто зробіть Yсинглтон!), Що в свою чергу призводить до труднощів з тестуванням / налагодженням та процедурним стилем програмування. Іноді необхідні одинарки; більшість часу, ні.
Aaronaught

3
Це одне із моїх стандартних запитань щодо інтерв'ю по телефону. Правильна відповідь: ніколи.
jonnii

3
@jonnii це добре, це допомагає попередити потенційних розробників, що таке бос!
Пан Хлопчик

Відповіді:


144

Синглтон - це клас, який дозволяє створити лише один екземпляр себе - і надає простий, простий доступ до зазначеного екземпляра. Приміщення для одиночної форми є закономірністю розвитку програмного забезпечення.

Існує реалізація C # "Впровадження схеми Singleton в C #", яка охоплює більшість того, що вам потрібно знати, включаючи кілька корисних порад щодо безпеки потоку .

Якщо чесно, то дуже рідко вам потрібно реалізувати синглтон - на мою думку, це має бути однією з тих речей, про які слід пам’ятати, навіть якщо вона використовується не надто часто.


2
приємний підручник, але святе лайно, що вони зробили з відступом коду
Inspi

Ось більш пряме посилання на те, що я вважаю ідеальною реалізацією в 2020 році. Тобто, " використання .NET 4 типу Lazy <T> ", а також посилання на Microsoft Doc для Lazy<T> Class.
Chiramisu

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.two нитка може викликати один і той же час і може створити два окремих об'єкти.
Алагесан Палані

5
@Alagesan Palani, справді ти маєш рацію. Я не досвідчений у деталях низького рівня ініціалізації рівня класу, але думаю, що зміна, яку я вніс, стосується проблеми, пов'язаної з безпекою потоку.
Кріс Сіммонс

3
звичайно, я НЕ вказую, що ти помилився. я даю підказку читачеві про безпеку ниток, щоб вони були обережні, якщо їм доведеться з цим боротися.
Алагесан Палані

9
Ні, я вважаю, що ваш коментар важливий. Зважаючи на те, що однотонний повинен доставити один - і лише один - екземпляр, умова гонки тут відкриває можливість доставки більше одного. Дивіться версію зараз із ініціалізацією статичного поля. Я вважаю, що це вирішує проблему безпеки потоку, якщо я читаю документи та це ТАК відповідаю правильно.
Кріс Сіммонс

1
@AlagesanPalani, я бачу, що ви заявили, що кілька інших відповідей не є безпечними для потоків. Чи хотіли б ви забезпечити безпечне рішення?
Bonez024,

39

Що це: Клас, для якого існує лише один стійкий екземпляр протягом усього життя програми. Дивіться Одинарний шаблон .

Коли слід використовувати: Як можна менше. Тільки коли ви абсолютно впевнені, що вам це потрібно. Я не хочу говорити "ніколи", але зазвичай є краща альтернатива, наприклад, ін'єкція залежності або просто статичний клас.


16
Я не впевнений, що статичний клас є кращою альтернативою, ніж синглтон ... це дійсно залежить від ситуації та мови.
marcgg

5
Статичні класи не поводяться так само, як синглтон, синглтон може бути переданий у методи як параметр, тоді як статичний клас не може.
TabbyCool

4
Погодьтеся з marcgg - я не вважаю статичний клас гарною альтернативою одиночним, тому що у вас все ще виникає проблема з подачею замінника, наприклад, під час тестування компонента, який залежить від цього класу. Але я також бачу різні способи використання, статичний клас зазвичай використовується для незалежних функцій утиліти, незалежних від стану, де сингтон - це фактичний екземпляр класу, і він, як правило, зберігає стан. Я повністю погоджуюся використовувати натомість DI, а потім скажу вашому контейнеру DI, що ви хочете, щоб він використовував лише один екземпляр цього класу.
Піт

9
Я відповів на цю відповідь, оскільки вона не дає мені інформації про те, коли її використовувати. "Тільки тоді, коли вам це потрібно" насправді не дає мені взагалі ніякої інформації для того, хто є новим для одиноких.
Серхіо Тапія

9
@Adkins: DI означає "Dependency Injection", тобто коли будь-які класові залежності передаються через (зазвичай) конструктор або публічну власність. Само по собі DI не вирішує проблему "відстань", але вона зазвичай реалізується поряд з контейнером Інверсії контролю (IoC), який знає, як автоматично ініціалізувати будь-які залежності. Отже, якщо ви створюєте Сінглтон для вирішення проблеми "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.two нитка може викликати один і той же час і може створити два окремих об'єкти.
Алагесан Палані

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 та Bar додатково книги від Джудіт Бішоп на C # 3.0 Design Patterns мають приклад про активне застосування в док-станції Mac.

Якщо ви подивитесь на код, ми фактично будуємо нові об’єкти для циклу, так що створюється новий об’єкт, але повторно використовується екземпляр, у результаті якого у старого балансовика та нового балансовика є той самий екземпляр, як? його завдяки статичному ключовому слову, що використовується у функції GetLoadBalancer () , незважаючи на те, що значення сервера є випадковим списком, статичне в GetLoadBalancer () належить саме типу, а не конкретному об'єкту.

Крім того , існує подвійна перевірка блокування тут

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

від MSDN

Ключове слово блокування забезпечує те, що один потік не вводить критичний розділ коду, а інший - у критичний розділ. Якщо інший потік намагається ввести заблокований код, він буде чекати, блокувати, поки об'єкт не буде звільнений.

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

Сподіваємось, це допомагає очистити більше.

І будь ласка, прокоментуйте, якщо я розумію, що я керую невірними способами.


6

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


5

Хоча тут може бути лише один екземпляр сингтона, він не є тотожним статичним класом. Статичний клас може містити лише статичні методи і ніколи не може бути інстанційним, тоді як екземпляр сингтона може використовуватися так само, як і будь-який інший об'єкт.


2

Це модель дизайну і вона не характерна для c #. Більше про це в Інтернеті та ТАК, як у цій статті у Вікіпедії .

В інженерії програмного забезпечення однотонний візерунок - це модель дизайну, яка використовується для обмеження інстанції класу одним об'єктом. Це корисно, коли для координації дій у всій системі потрібен саме один об'єкт. Концепція іноді узагальнюється до систем, які працюють більш ефективно, коли існує лише один об’єкт, або обмежують інстанціювання певною кількістю об'єктів (скажімо, п'ятьма). Деякі вважають це анти-зразком, вважаючи, що це надмірно використовує, вводить зайві обмеження в ситуаціях, коли єдиний екземпляр класу фактично не потрібен, і вводить глобальний стан у додаток.

Ви повинні використовувати його, якщо хочете клас, який можна інстанціювати лише один раз.


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

Що таке синглтон:
це клас, який дозволяє створювати лише один екземпляр самого себе, і, як правило, надає простий доступ до цього екземпляра.

Коли слід використовувати:
Це залежить від ситуації.

Примітка. Будь ласка, не використовуйте для з'єднання db, для детальної відповіді зверніться до відповіді @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>


1

Ось що таке одиночний: http://en.wikipedia.org/wiki/Singleton_pattern

Я не знаю C #, але насправді це одне й те саме на всіх мовах, лише реалізація відрізняється.

Як правило, слід уникати одиночних, коли це можливо, але в деяких ситуаціях це дуже зручно.

Вибачте за мою англійську ;)


у вас англійська нормально :)
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; }            
    }
}

У цій статті описано, як ми можемо створити безпечний однотонний клас за допомогою потокової змінної та їх практичне використання в додатках.


1

Я знаю, що дуже пізно відповісти на питання, але з Auto-Property ви можете зробити щось подібне:

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

Де Singletonви займаєтесь класом і може бути через, у цьому випадку властивість readonly Instance.


0

EX Ви можете використовувати Singleton для глобальної інформації, яку потрібно вводити.

У моєму випадку я зберігав дані про зареєстрованого користувача (ім’я користувача, дозволи тощо) у глобальному статичному класі. І коли я намагався реалізувати Unit Test, я не мав можливості ввести залежність до класів Controller. Таким чином я змінив свій статичний клас на шаблон 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


0

Нам потрібно використовувати шаблон дизайну Singleton в C #, коли нам потрібно переконатися, що буде створено лише один екземпляр певного класу, а потім забезпечити простий глобальний доступ до цього примірника для всієї програми.

Сценарії в режимі реального часу, де можна використовувати шаблон дизайну Singleton: Сервісні проксі: Як ми знаємо, виклик сервісного API - це велика операція в додатку. Процес, який займає більшу частину часу, - це створення клієнтського сервісу для виклику сервісного API. Якщо ви створите серверний проксі як Singleton, це підвищить продуктивність вашої програми.

Фасади: Ви також можете створити підключення до Бази даних як Singleton, що може підвищити продуктивність програми.

Журнали. У програмі виконання операцій вводу / виводу на файлі - це дорога операція. Якщо ви створили Logger як Singleton, це підвищить ефективність операцій вводу / виводу.

Обмін даними: Якщо у вас є постійні значення або значення конфігурації, ви можете зберегти ці значення в Singleton, щоб вони могли прочитати інші компоненти програми.

Кешування: Як ми знаємо, отримання даних із бази даних - це трудомісткий процес. У вашій програмі ви можете кешувати головну та конфігураційну пам'ять, що дозволить уникнути дзвінків DB. У таких ситуаціях клас Singleton може бути використаний для ефективного кешування кешування з синхронізацією потоку, що різко підвищує продуктивність програми.

Недоліки схеми дизайну Singleton у C # Недоліками використання шаблону дизайну Singleton у C # є такі:

Тестування одиниць дуже складно, оскільки це вносить глобальний стан у додаток. Це зменшує потенціал паралелізму в межах програми, оскільки для доступу до екземпляра singleton у багатопотоковому середовищі потрібно серіалізувати об'єкт за допомогою блокування.

Я взяв це з наступної статті.

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


0

Нитка безпечна синглтон, не використовуючи замки та не ліниві екземпляри.

Ця реалізація має статичний конструктор, тому вона виконується лише один раз на Домен додатка.

public sealed class Singleton
{

    static Singleton(){}

    private Singleton(){}

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

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.