Che cos'è un singleton in C #?


182

Che cos'è un Singleton e quando dovrei usarlo?



4
Inoltre, Singleton è uno dei modelli di progettazione più utilizzati e abusati della programmazione OO.
ChaosPandion,

3
@Fabiano: Perché ha un modo per creare accoppiamenti che non hanno senso (come posso Xparlare Y? Basta fare Yun singleton!), Che a sua volta porta a difficoltà di test / debugging e uno stile procedurale di programmazione. A volte sono necessari i Singleton; il più delle volte no.
Aaronaught,

3
Questa è una delle mie domande standard sul colloquio telefonico. La risposta corretta è: mai.
jonnii,

3
@jonnii va bene, aiuta ad avvertire i potenziali sviluppatori come è il loro capo!
Mr. Boy,

Risposte:


145

Un singleton è una classe che consente di creare solo un'istanza di se stessa e fornisce un accesso semplice e facile a tale istanza. La premessa singleton è un modello per lo sviluppo del software.

Esiste un'implementazione in C # "Implementare il modello Singleton in C #" che copre la maggior parte di ciò che devi sapere, compresi alcuni buoni consigli sulla sicurezza del thread .

Ad essere onesti, è molto raro che tu debba implementare un singleton - secondo me dovrebbe essere una di quelle cose di cui dovresti essere a conoscenza, anche se non viene usato troppo spesso.


2
bel tutorial ma merda santa cosa hanno fatto per il rientro del codice
Inspi

Ecco un link più diretto a quella che considero l'implementazione ideale nel 2020. Cioè, " usando il tipo Lazy <T> di .NET 4 ", così come il link a Microsoft Doc per il Lazy<T> Class.
Chiramisu,

52

Hai chiesto C #. Esempio di prova:


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

    private static readonly Singleton _singleton = new Singleton();

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

14
non thread safe. due thread possono chiamare contemporaneamente e possono creare due oggetti separati.
Alagesan Palani,

5
@Alagesan Palani, in effetti hai ragione. Non sono competente nei dettagli di basso livello dell'inizializzazione a livello di classe, ma penso che la modifica che ho apportato risolva il problema della sicurezza dei thread.
Chris Simmons,

3
certo, NON sto indicando che hai torto. sto dando un suggerimento al lettore sulla sicurezza dei thread, in modo che possano stare attenti se devono occuparsene.
Alagesan Palani,

9
No, penso che il tuo commento sia importante. Dato che un singleton dovrebbe fornire una - e solo una - istanza, le condizioni di gara qui aprono la possibilità che ne venga consegnata più di una. Guarda subito la versione, con l'inizializzazione del campo statico. Credo che questo risolva il problema di sicurezza del thread, se leggo i documenti e questa risposta SO in modo corretto.
Chris Simmons,

1
@AlagesanPalani, vedo che hai affermato che molte altre risposte non sono thread-safe. Ti andrebbe di fornire una soluzione sicura per i thread?
Bonez024,

39

Che cos'è: una classe per la quale esiste solo un'istanza persistente per tutta la durata di un'applicazione. Vedi Singleton Pattern .

Quando dovresti usarlo: il meno possibile. Solo quando sei assolutamente certo di averne bisogno. Sono riluttante a dire "mai", ma di solito esiste un'alternativa migliore, come l'iniezione di dipendenza o semplicemente una classe statica.


16
Non sono sicuro che una classe statica sia un'alternativa migliore di una singleton ... dipende davvero dalla situazione e dal linguaggio.
marcgg,

5
Le classi statiche non si comportano allo stesso modo di un singleton, un singleton può essere passato ai metodi come parametro mentre una classe statica non può.
TabbyCool

4
In accordo con marcgg - Non vedo una classe statica come una buona alternativa ai singoli, perché hai ancora il problema di fornire un sostituto, ad esempio durante il test di un componente che dipende da questa classe. Ma vedo anche usi diversi, una classe statica verrebbe generalmente utilizzata per funzioni di utilità indipendenti che sono indipendenti dallo stato, in cui un singleton è un'istanza di classe effettiva e in genere memorizza uno stato. Sono completamente d'accordo a utilizzare DI invece, e quindi dico al tuo contenitore DI che vuoi che usi solo una singola istanza di quella classe.
Pete,

9
Ho votato verso il basso questa risposta perché non mi dà informazioni su quando usarla. "Solo quando ne hai bisogno" in realtà non mi dà alcuna informazione per qualcuno che è nuovo ai single.
Sergio Tapia,

9
@Adkins: DI sta per Dependency Injection, ovvero quando tutte le dipendenze di classe passano attraverso (di solito) un costruttore o una proprietà pubblica. DI da solo non risolve il problema della "distanza", ma di solito viene implementato insieme a un contenitore Inversion-of-Control (IoC) che sa come inizializzare automaticamente eventuali dipendenze. Quindi, se stai creando un Singleton per risolvere il problema "X non sa come trovare / parlare con Y", una combinazione di DI e IoC può risolvere lo stesso problema con un accoppiamento più lento.
Aaronaught il

27

un altro modo per implementare singleton in c #, preferisco personalmente questo modo perché puoi accedere all'istanza della classe singeton come proprietà anziché come metodo.

public class Singleton
    {
        private static Singleton instance;

        private Singleton() { }

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

        //instance methods
    }

ma bene, per quanto ne so entrambi i modi sono considerati "giusti", quindi è solo una questione di sapore personale.


11
non thread safe. due thread possono chiamare contemporaneamente e possono creare due oggetti separati.
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();
        }
    }
}

Ho preso il codice da dofactory.com , niente di così fantasioso, ma lo trovo di gran lunga migliore degli esempi con Foo and Bar, inoltre il libro di Judith Bishop su C # 3.0 Design Patterns ha un esempio sull'applicazione attiva in Mac Dock.

Se osservi il codice, in realtà stiamo costruendo nuovi oggetti per il ciclo, in modo da creare un nuovo oggetto ma riutilizzare l'istanza a seguito della quale oldbalancer e newbalancer hanno la stessa istanza, come? è dovuto alla parola chiave statica utilizzata sulla funzione GetLoadBalancer () , nonostante abbia un valore del server diverso che è un elenco casuale, statico su GetLoadBalancer () appartiene al tipo stesso piuttosto che a un oggetto specifico.

Inoltre c'è un doppio controllo di blocco qui

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

da allora da MSDN

La parola chiave lock assicura che un thread non entri in una sezione critica del codice mentre un altro thread si trova nella sezione critica. Se un altro thread tenta di inserire un codice bloccato, attenderà, bloccherà, fino al rilascio dell'oggetto.

quindi ogni volta che viene emesso un blocco di esclusione reciproca, anche se non è necessario, il che non è necessario, quindi abbiamo un controllo nullo.

Speriamo che aiuti a chiarire di più.

E per favore commenta se la mia comprensione sta indirizzando modi sbagliati.


6

Un Singleton (e questo non è legato a C #, è un modello di progettazione OO) è quando si desidera consentire a UNA sola istanza di una classe di essere creata in tutta l'applicazione. Gli utilizzi in genere includono risorse globali, sebbene, per esperienza personale, dirò che spesso sono fonte di grande dolore.


5

Sebbene ci possa essere solo un'istanza di un singleton, non è la stessa di una classe statica. Una classe statica può contenere solo metodi statici e non può mai essere istanziata, mentre l'istanza di un singleton può essere utilizzata allo stesso modo di qualsiasi altro oggetto.


2

È un modello di progettazione e non è specifico per c #. Maggiori informazioni su Internet e SO, come in questo articolo di Wikipedia .

Nell'ingegneria del software, il modello singleton è un modello di progettazione utilizzato per limitare l'istanza di una classe a un oggetto. Ciò è utile quando è necessario esattamente un oggetto per coordinare le azioni nel sistema. Il concetto è talvolta generalizzato a sistemi che funzionano in modo più efficiente quando esiste un solo oggetto o che limitano l'istanza a un certo numero di oggetti (diciamo cinque). Alcuni lo considerano un anti-modello, ritenendo che sia abusato, introducono limitazioni non necessarie in situazioni in cui non è richiesta una sola istanza di una classe e introducono lo stato globale in un'applicazione.

Dovresti usarlo se vuoi una classe che può essere istanziata una sola volta.


2

Lo uso per i dati di ricerca. Carica una volta dal 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

Che cos'è un singleton:
è una classe che consente di creare solo un'istanza di se stessa e in genere consente un accesso semplice a tale istanza.

Quando usare:
dipende dalla situazione.

Nota: non utilizzare sulla connessione db, per una risposta dettagliata fare riferimento alla risposta di @Chad Grant

Ecco un semplice esempio di a 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;
        }
    }
}

Puoi anche usare Lazy<T>per creare il tuo Singleton.

Vedi qui per un esempio più dettagliato usandoLazy<T>


1

Ecco cos'è singleton: http://en.wikipedia.org/wiki/Singleton_pattern

Non conosco C #, ma in realtà è la stessa cosa in tutte le lingue, solo l'implementazione differisce.

Dovresti generalmente evitare singleton quando è possibile, ma in alcune situazioni è molto conveniente.

Mi scusi per il mio inglese ;)


il tuo inglese è OK :)
FrenkyB,

1

La classe Singleton viene utilizzata per creare una singola istanza per l'intero dominio dell'applicazione.

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

In questo articolo viene descritto come possiamo creare una classe singleton thread-safe usando la variabile readonly e il loro uso pratico nelle applicazioni.


1

So che è molto tardi per rispondere alla domanda, ma con Auto-Property puoi fare qualcosa del genere:

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

Dov'è la Singletontua classe e può essere via, in questo caso la proprietà di sola lettura Instance.


0

EX È possibile utilizzare Singleton per le informazioni globali che devono essere iniettate.

Nel mio caso, stavo mantenendo i dettagli dell'utente registrato (nome utente, autorizzazioni, ecc.) Nella classe statica globale. E quando ho provato a implementare il Test unit, non c'era modo di iniettare dipendenza nelle classi Controller. Così ho cambiato la mia classe statica in modello 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

Dobbiamo usare il Singleton Design Pattern in C # quando dobbiamo assicurarci che verrà creata solo un'istanza di una particolare classe e quindi fornire un semplice accesso globale a quell'istanza per l'intera applicazione.

Scenari in tempo reale in cui è possibile utilizzare il modello di progettazione Singleton: proxy di servizio: come sappiamo invocare un'API di servizio è un'operazione estesa in un'applicazione. Il processo che impiega la maggior parte del tempo sta creando il client di servizio al fine di invocare l'API di servizio. Se si crea il proxy di servizio come Singleton, le prestazioni dell'applicazione miglioreranno.

Facciate: è anche possibile creare le connessioni al database come Singleton che possono migliorare le prestazioni dell'applicazione.

Registri: in un'applicazione, eseguire l'operazione I / O su un file è un'operazione costosa. Se si crea il Logger come Singleton, le prestazioni dell'operazione di I / O miglioreranno.

Condivisione dei dati: se si hanno valori costanti o valori di configurazione, è possibile conservare questi valori in Singleton in modo che possano essere letti da altri componenti dell'applicazione.

Memorizzazione nella cache: come sappiamo recuperare i dati da un database è un processo che richiede tempo. Nella tua applicazione, puoi memorizzare nella cache il master e la configurazione che eviteranno le chiamate DB. In tali situazioni, la classe Singleton può essere utilizzata per gestire la memorizzazione nella cache con la sincronizzazione dei thread in modo efficiente che migliora drasticamente le prestazioni dell'applicazione.

Svantaggi del Singleton Design Pattern in C # Gli svantaggi dell'utilizzo del Singleton Design Pattern in C # sono i seguenti:

Il test unitario è molto difficile perché introduce uno stato globale in un'applicazione. Riduce il potenziale di parallelismo all'interno di un programma perché per accedere all'istanza singleton in un ambiente multi-thread, è necessario serializzare l'oggetto utilizzando il blocco.

Ho preso questo dal seguente articolo.

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


0

Thread Safe Singleton senza usare blocchi e nessuna istanza pigra.

Questa implementazione ha un costruttore statico, quindi viene eseguita una sola volta per Dominio Applicazione.

public sealed class Singleton
{

    static Singleton(){}

    private Singleton(){}

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.