Co to jest singleton w C #?


181

Co to jest Singleton i kiedy powinienem go używać?



4
Ponadto Singleton jest jednym z najczęściej używanych i nadużywanych wzorców projektowych w programowaniu OO.
ChaosPandion

3
@Fabiano: Ponieważ ma sposób na tworzenie połączeń, które nie mają sensu (jak mogę Xrozmawiać Y? Po prostu zrób Ysingleton!), Co z kolei prowadzi do trudności w testowaniu / debugowaniu i proceduralnym stylu programowania. Czasami konieczne są Singletony; przez większość czasu nie.
Aaronaught

3
To jedno z moich standardowych pytań do rozmowy telefonicznej. Prawidłowa odpowiedź to: nigdy.
jonnii

3
@jonnii to dobrze, pomaga ostrzec potencjalnych programistów, jaki jest szef!
Mr. Boy

Odpowiedzi:


144

Singleton to klasa, która pozwala na utworzenie tylko jednej instancji samego siebie - i daje prosty, łatwy dostęp do tej instancji. Założeniem singletonu jest wzorzec rozwoju oprogramowania.

Istnieje implementacja C # „Implementacja wzorca Singleton w C #” obejmująca większość tego, co musisz wiedzieć - w tym kilka dobrych rad dotyczących bezpieczeństwa wątków .

Szczerze mówiąc, bardzo rzadko trzeba wdrożyć singletona - moim zdaniem powinna to być jedna z tych rzeczy, o których powinieneś wiedzieć, nawet jeśli nie jest to używane zbyt często.


2
fajny samouczek, ale cholera, co zrobili z wcięciem kodu
Inspi

Oto bardziej bezpośredni link do tego, co uważam za idealną implementację w 2020 roku. To znaczy „ przy użyciu typu Lazy <T> .NET 4 ”, a także link do dokumentu Microsoft Doc dla Lazy<T> Class.
Chiramisu,

52

Poprosiłeś o C #. Trywialny przykład:


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

    private static readonly Singleton _singleton = new Singleton();

    public static Singleton GetSingleton()
    {
        return _singleton;
    }
}

14
nie wątek bezpieczny. dwa wątki mogą wywoływać w tym samym czasie i mogą tworzyć dwa osobne obiekty.
Alagesan Palani,

5
@Alagesan Palani, rzeczywiście masz rację. Nie jestem biegły w szczegółach niskiego poziomu inicjalizacji na poziomie klasy, ale myślę, że wprowadzona przeze mnie zmiana rozwiązuje problem bezpieczeństwa wątków.
Chris Simmons,

3
jasne, NIE sugeruję, że się mylisz. daję wskazówkę czytelnikowi na temat bezpieczeństwa wątków, aby byli ostrożni, gdyby musieli sobie z tym poradzić.
Alagesan Palani,

9
Nie, myślę, że twój komentarz jest ważny. Biorąc pod uwagę, że singleton ma dostarczyć jeden - i tylko jeden - przypadek, warunki wyścigu tutaj otwierają możliwość dostarczenia więcej niż jednego. Zobacz teraz wersję ze statyczną inicjalizacją pola. Wierzę, że to rozwiązuje problem bezpieczeństwa wątków, jeśli poprawnie przeczytam dokumentację i odpowiedź SO .
Chris Simmons,

1
@AlagesanPalani, widzę, że stwierdziłeś, że kilka innych odpowiedzi nie jest bezpiecznych dla wątków. Czy chciałbyś zapewnić rozwiązanie, które jest bezpieczne dla wątków?
Bonez024,

39

Co to jest: Klasa, dla której istnieje tylko jedna, trwała instancja przez cały okres istnienia aplikacji. Zobacz Singleton Pattern .

Kiedy należy go używać: jak najmniej. Tylko wtedy, gdy masz absolutną pewność , że go potrzebujesz. Niechętnie mówię „nigdy”, ale zwykle istnieje lepsza alternatywa, taka jak Dependency Injection lub po prostu klasa statyczna.


16
Nie jestem pewien, czy klasa statyczna jest lepszą alternatywą niż singleton ... to naprawdę zależy od sytuacji i języka.
marcgg

5
Klasy statyczne nie zachowują się w taki sam sposób jak singleton, singleton można przekazać do metod jako parametr, podczas gdy klasa statyczna nie.
TabbyCool

4
Zgadzam się z marcgg - Nie widzę klasy statycznej jako dobrej alternatywy dla singletonów, ponieważ nadal masz problem z dostarczeniem zamiennika, np. Podczas testowania komponentu zależnego od tej klasy. Ale widzę też różne zastosowania, klasa statyczna byłaby zwykle używana do niezależnych funkcji narzędziowych, które są niezależne od stanu, gdzie singleton jest rzeczywistą instancją klasy i zwykle przechowuje stan. Całkowicie zgadzam się na użycie zamiast tego DI, a następnie mówię Twojemu kontenerowi DI, że chcesz używać tylko jednego wystąpienia tej klasy.
Pete

9
Głosowałem za odpowiedzią, ponieważ nie daje mi ona informacji o tym, kiedy jej użyć. „Tylko wtedy, gdy jest to potrzebne”, tak naprawdę nie daje mi żadnych informacji dla kogoś, kto jest singlem.
Sergio Tapia

9
@Adkins: DI oznacza Dependency Injection, czyli kiedy zależności klas są przekazywane przez (zwykle) konstruktor lub właściwość publiczną. Sam DI nie rozwiązuje problemu „odległości”, ale zwykle jest implementowany wraz z kontenerem Inversion-of-Control (IoC), który wie, jak automatycznie inicjować wszelkie zależności. Więc jeśli tworzysz Singletona w celu rozwiązania problemu „X nie wie, jak znaleźć / rozmawiać z Y”, połączenie DI i IoC może rozwiązać ten sam problem z luźniejszym sprzężeniem.
Aaronaught

27

inny sposób implementacji singletonu w języku c #, osobiście wolę ten sposób, ponieważ można uzyskać dostęp do instancji klasy singeton jako właściwość zamiast metody.

public class Singleton
    {
        private static Singleton instance;

        private Singleton() { }

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

        //instance methods
    }

ale cóż, o ile wiem, oba sposoby są uważane za „właściwe”, więc jest to kwestia osobistego smaku.


11
nie wątek bezpieczny. dwa wątki mogą wywoływać w tym samym czasie i mogą tworzyć dwa osobne obiekty.
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();
        }
    }
}

Wziąłem kod z dofactory.com , nic nadzwyczajnego, ale uważam, że jest to tak dobre, jak przykłady z Foo i Barem, a dodatkowo książka Judith Bishop na temat C # 3.0 Design Patterns zawiera przykład aktywnej aplikacji w Mac Docku.

Jeśli spojrzysz na kod, w rzeczywistości budujemy nowe obiekty dla pętli for , więc tworzy nowy obiekt, ale wykorzystuje ponownie instancję, w wyniku czego oldbalancer i newbalancer mają tę samą instancję. Jak? ze względu na statyczne słowo kluczowe użyte w funkcji GetLoadBalancer () , mimo że ma inną wartość serwera, która jest losową listą, static w GetLoadBalancer () należy do samego typu, a nie do konkretnego obiektu.

Dodatkowo jest tutaj blokada podwójnej kontroli

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

ponieważ z MSDN

Słowo kluczowe lock zapewnia, że ​​jeden wątek nie wchodzi w krytyczną sekcję kodu, podczas gdy inny wątek znajduje się w sekcji krytycznej. Jeśli inny wątek spróbuje wprowadzić zablokowany kod, zaczeka, zablokuje, aż obiekt zostanie zwolniony.

więc za każdym razem wydawana jest blokada wzajemnego wykluczenia, nawet jeśli nie jest to konieczne, więc mamy kontrolę zerową.

Mam nadzieję, że pomoże to w oczyszczeniu więcej.

I proszę o komentarz, jeśli rozumiem, że źle kieruję.


6

Singleton (i to nie jest związane z C #, to wzorzec projektowy OO) ma miejsce, gdy chcesz zezwolić na utworzenie tylko jednej instancji klasy w całej aplikacji. Zastosowania zwykle obejmowałyby zasoby globalne, chociaż powiem z własnego doświadczenia, bardzo często są źródłem wielkiego bólu.


5

Chociaż może istnieć tylko jeden przypadek singletonu, nie jest to to samo, co klasa statyczna. Klasa statyczna może zawierać tylko metody statyczne i nigdy nie może być utworzona instancja, natomiast instancja singletonu może być używana w taki sam sposób, jak każdy inny obiekt.


2

Jest to wzorzec projektowy i nie jest specyficzny dla c #. Więcej o tym w całym Internecie i SO, tak jak w tym artykule na Wikipedii .

W inżynierii oprogramowania wzorzec singletonu jest wzorcem projektowym używanym do ograniczenia tworzenia instancji klasy do jednego obiektu. Jest to przydatne, gdy potrzebny jest dokładnie jeden obiekt do koordynowania działań w całym systemie. Pojęcie to jest czasem uogólnione na systemy, które działają wydajniej, gdy istnieje tylko jeden obiekt lub które ograniczają tworzenie instancji do określonej liczby obiektów (powiedzmy pięć). Niektórzy uważają, że jest to anty-wzorzec, sądząc, że jest nadużywany, wprowadza niepotrzebne ograniczenia w sytuacjach, w których jedna instancja klasy nie jest tak naprawdę wymagana, i wprowadza stan globalny do aplikacji.

Powinieneś go użyć, jeśli chcesz mieć klasę, którą można założyć tylko raz.


2

Używam go do wyszukiwania danych. Załaduj raz z DB.

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

Co to jest singleton:
Jest to klasa, która pozwala na utworzenie tylko jednego wystąpienia i zwykle daje prosty dostęp do tego wystąpienia.

Kiedy należy używać:
To zależy od sytuacji.

Uwaga: nie używaj połączenia DB, aby uzyskać szczegółową odpowiedź, zapoznaj się z odpowiedzią @Chad Grant

Oto prosty przykład 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;
        }
    }
}

Możesz także użyć Lazy<T>do utworzenia swojego Singleton.

Tutaj znajdziesz bardziej szczegółowy przykład użyciaLazy<T>


1

Oto czym jest singleton: http://en.wikipedia.org/wiki/Singleton_pattern

Nie znam C #, ale tak naprawdę jest to samo we wszystkich językach, różni się tylko implementacja.

Ogólnie należy unikać singletonu, gdy jest to możliwe, ale w niektórych sytuacjach jest to bardzo wygodne.

Przepraszam za mój angielski ;)


Twój angielski jest OK :)
FrenkyB

1

Klasa Singleton służy do utworzenia pojedynczego wystąpienia dla całej domeny aplikacji.

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; }            
    }
}

W tym artykule opisano, w jaki sposób możemy stworzyć bezpieczną dla wątków klasę singletonu, używając zmiennej tylko do odczytu i ich praktycznego zastosowania w aplikacjach.


1

Wiem, że jest bardzo późno, aby odpowiedzieć na pytanie, ale dzięki Auto-Property możesz zrobić coś takiego:

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

Gdzie Singletonjest twoja klasa i może być przez, w tym przypadku własność tylko do odczytu Instance.


0

EX Możesz użyć Singleton do globalnych informacji, które należy wstrzyknąć.

W moim przypadku zachowałem szczegóły zalogowanego użytkownika (nazwa użytkownika, uprawnienia itp.) W globalnej klasie statycznej. A kiedy próbowałem zaimplementować test jednostkowy, nie było mowy o wstrzyknięciu zależności do klas kontrolerów. W ten sposób zmieniłem wzór klasy statycznej na 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

Musimy użyć wzorca projektowego Singleton w języku C #, gdy musimy zapewnić, że zostanie utworzone tylko jedno wystąpienie określonej klasy, a następnie zapewnić prosty globalny dostęp do tego wystąpienia dla całej aplikacji.

Scenariusze w czasie rzeczywistym, w których można użyć wzorca projektowego Singleton: Proxy usług: jak wiemy, wywoływanie interfejsu API usługi jest rozległą operacją w aplikacji. Proces, który zajmuje większość czasu, polega na utworzeniu klienta usługi w celu wywołania interfejsu API usługi. Jeśli utworzysz serwer proxy usługi jako Singleton, poprawi to wydajność Twojej aplikacji.

Fasady: Możesz także utworzyć połączenia z bazą danych jako Singleton, co może poprawić wydajność aplikacji.

Dzienniki: w aplikacji wykonywanie operacji we / wy na pliku jest kosztowną operacją. Jeśli utworzysz Logger jako Singleton, poprawi to wydajność operacji I / O.

Udostępnianie danych: jeśli masz jakieś stałe wartości lub wartości konfiguracyjne, możesz zachować te wartości w Singleton, aby mogły być odczytane przez inne komponenty aplikacji.

Buforowanie: jak wiemy, pobieranie danych z bazy danych jest procesem czasochłonnym. W aplikacji możesz buforować wzorzec i konfigurację w pamięci, co pozwoli uniknąć wywołań DB. W takich sytuacjach klasa Singleton może być użyta do obsługi buforowania z synchronizacją wątków w efektywny sposób, co znacznie poprawia wydajność aplikacji.

Wady wzorca projektowego Singleton w C # Wady używania wzorca projektowego Singleton w C # są następujące:

Testowanie jednostkowe jest bardzo trudne, ponieważ wprowadza do aplikacji stan globalny. Zmniejsza to potencjał równoległości w programie, ponieważ aby uzyskać dostęp do instancji singletonu w środowisku wielowątkowym, należy serializować obiekt za pomocą blokowania.

Wziąłem to z następującego artykułu.

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


0

Wątek Bezpieczny Singleton bez użycia blokad i bez leniwej instancji.

Ta implementacja ma statycznego konstruktora, więc jest wykonywana tylko raz na domenę aplikacji.

public sealed class Singleton
{

    static Singleton(){}

    private Singleton(){}

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

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.