Ein Singleton-Muster in C # implementieren

27874
Aim Kai

Ist dies der beste Weg, um dieses Muster in C # zu implementieren?

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
    public static Singleton Instance { get { return instance; } }

    static Singleton() {}
    private Singleton() {}
}
Antworten
67
[Singleton ist ein Antipattern] (http://thetechcandy.wordpress.com/2009/12/02/singletons-is-anti-pattern/). Arjang vor 7 Jahren 8
Bitte beachten Sie: ["Global State and Singletons"] (http://www.youtube.com/watch?v=-FRm3VPhseI) tereško vor 7 Jahren 0

9 Antworten auf die Frage

75
Zolomon

Ich verwende Jon Skeets Version eines Thread-sicheren Singleton mit vollständig lazy Instantiation in C #:

public sealed class Singleton
{
    // Thread safe Singleton with fully lazy instantiation á la Jon Skeet:
    // http://csharpindepth.com/Articles/General/Singleton.aspx
    Singleton()
    {
    }

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

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

        internal static readonly Singleton instance = new Singleton();
    }
}

Es wirkt Wunder für mich! Es ist wirklich einfach zu bedienen, holen Sie die Instanz einfach aus der Instance-Eigenschaft.SingletonName instance = SingletonName.Instance;

Wäre GetInstance nicht eher für einen Accessor geeignet? Oder unterscheidet sich diese Konvention in C #? adamk vor 9 Jahren 0
Ich denke, dass Instance ausreicht, aber ich denke, YMMV. Zolomon vor 9 Jahren 5
Gibt es bestimmte Vorteile beim Erstellen einer verschachtelten Klasse? Adam Lear vor 9 Jahren 0
@AnnaLear: Ich glaube, dass Jon Skeet es selbst besser erklärt als ich im Moment kann, checkt den Link. :) Zolomon vor 9 Jahren 0
Die verschachtelte Klasse macht es faul. Die Laufzeit wird die Singleton-Instanz erst instanziieren, wenn Singleton.Instance aufgerufen wird. Wenn Sie also andere statische Member oder etwas in Singleton haben, können Sie darauf zugreifen, ohne die Singleton-Instanz zu erstellen. Wes P vor 9 Jahren 3
Ich möchte jeden dazu ermutigen, den Artikel von Jon Skeet (unter dem Link, den Zolomon angegeben hat) zu lesen, da er fast alle Antworten in diesem Thema enthält und alle Vor- und Nachteile auflistet. ShdNx vor 9 Jahren 9
Dies ist auch threadsicher. David Basarab vor 9 Jahren 2
Das ist verdammt clever. Aber ich musste Jons Artikel lesen, um wirklich zu verstehen, was los ist. Aber der nächste Typ, der Ihren Code verwaltet, wird wahrscheinlich nicht alle Tricks bekommen, wenn Sie ihn nur ansehen. Ich würde einen Kommentar hinzufügen, der auf Jons Artikel verweist. AngryHacker vor 9 Jahren 0
@AngryHacker: Das mache ich in meinen Implementierungen. ;) Zolomon vor 9 Jahren 0
Wie würdest du das in Tests einsetzen - was wäre, wenn du es oder Teile davon verspotten müsstest? Chris Moschini vor 8 Jahren 1
@Chris: Das würdest du nicht. Das ist ein Grund, warum Singletons scheiße sind - sie sind im Wesentlichen verborgene globale Zustände, so dass es fast unmöglich ist, Code, der sie enthält, zuverlässig zu testen. cHao vor 8 Jahren 7
@cHao +1 gute Arbeit, um auf die Saugkraft von Singletons hinzuweisen! MattDavey vor 8 Jahren 0
Es ist eigentlich gar nicht so schlimm. Lassen Sie die Singleton-Klasse eine Schnittstelle implementieren und diese Schnittstelle simulieren. Es ist nicht so, dass jede Version der Klasse statisch ist, sondern nur eine Instanz. Jesse C. Slicer vor 8 Jahren 0
@Jesse: Das einzige Problem ist, dass Code, der Singletons verwendet, fast immer dazu führt, dass er sie selbst bekommt. Am Ende werden Anrufe an `Singleton.Instance` verstreut. Sie können sich nicht so leicht darüber lustig machen. Wenn erwartet wurde, dass der Code an ein Singleton übergeben wird, das er verwenden würde, könnten Sie sich sicher darüber lustig machen. An diesem Punkt haben Sie jedoch Abhängigkeitsinjektion und benötigen kein Singleton mehr. cHao vor 8 Jahren 1
@cHao Nicht ganz richtig; Einige kostenpflichtige Testprodukte können statische Klassen simulieren, und Pex kann statische Methoden simulieren: http://research.microsoft.com/en-us/projects/pex/ Chris Moschini vor 8 Jahren 0
@ChrisMoschini: Ja, du kannst es ausmalen, auspexen oder sogar den alten TypeMock-Isolator verwenden, aber wenn du ihn ersetzen musst, dann .. STRG-UMSCHALT-H ... :( Andrei Rinea vor 7 Jahren 0
Versuchen Sie, "Ambient Context" zu googeln, um ein paar gute Ideen zu erhalten, wie Sie einen abstrakten Typ zurückgeben können, der zur Laufzeit einen konkreten Standardwert hat, der jedoch zum Testen leicht austauschbar ist. ardave vor 6 Jahren 1
18
Martin

Ich benutze dies immer, es erlaubt eine faule Initialisierung von Generika, anstatt für jeden Typ von Singleton, den ich möchte, eine neue Singleton-Klasse zu erstellen. Es ist auch threadsicher, verwendet jedoch nicht für jeden Zugriff Sperren.

public static class Singleton<T> where T : class, new()
{
    private T instance = null;

    public T Instance
    {
        get
        {
            if (instance == null)
                Interlocked.CompareExchange(ref instance, new T(), null);

            return instance;
        }
    }
}

Wenn Sie mit der verriegelten Klasse nicht vertraut sind, führt sie atomare Operationen auf eine Weise durch, die häufig schneller als eine Sperre ist. Drei mögliche Fälle:

1) Erster Zugriff durch einen einzelnen Thread . Wahrscheinlich ungefähr dieselbe Leistung wie die naheliegende Methode, die ein Schloss verwendet

2) Erster Zugriff von vielen Threads gleichzeitig . Viele Threads treten möglicherweise in den verriegelten Austausch ein. In diesem Fall werden möglicherweise mehrere Elemente konstruiert, aber nur 1 "gewinnt". Solange Ihr Konstruktor keine globalen Nebenwirkungen hat (was eigentlich nicht der Fall sein sollte), ist das Verhalten korrekt. Die Leistung ist aufgrund von Mehrfachzuweisungen etwas geringer als eine Sperre, aber der Aufwand ist gering und dies ist ein sehr seltener Fall.

3) Spätere Zugriffe . Keine sperrenden oder verriegelten Operationen, dies ist ziemlich optimal und ist offensichtlich der Großteil.

+1 für eine gute Lösung, aber das Problem # 2, dass Ihre Konstrukteure keine Nebenwirkungen haben müssen, hat mich immer von diesem Ansatz abgehalten. GWLlosa vor 9 Jahren 0
Wie oft haben Ihre Konstrukteure Nebenwirkungen? Ich habe das immer für einen ziemlich schlechten Codegeruch gehalten. Besonders in Multithread-Code! Martin vor 9 Jahren 1
Darüber hinaus kann Ihr Konstruktor Nebeneffekte haben, solange sie threadsicher sind und nicht davon ausgehen, dass dies die einzige Instanz ist, die erstellt wird. Denken Sie daran, dass mehrere Instanzen erstellt werden können, aber nur eine "gewinnt". Wenn Sie also Nebenwirkungen haben, sollten sie besser implementiert werden! Martin vor 9 Jahren 0
@ Martin: Ist es nicht der Sinn von Singletons, dass nur eine Instanz erstellt werden kann? Es scheint mir, dass es ziemlich vernünftig ist, sich auf diese Annahme zu verlassen ... cHao vor 8 Jahren 0
@cHao wahr, aber wie ich schon sagte, betrachte ich irgendwelche Nebenwirkungen in einem Konstruktor, die über die Instanz hinausgehen, die einen der schlimmsten Codegerüche auslöst, doppelt so in Multithread-Code. Angenommen, wir stimmen darin überein, dass die Erstellung von 2 Instanzen keine Auswirkungen auf Ihr Programm haben kann. Martin vor 8 Jahren 0
-1, Nicht threadsicher (Konstruktor kann mehrmals aufgerufen werden), wodurch der Zweck eines Singleton-Objekts missachtet wird. Konrad Rudolph vor 7 Jahren 1
13
Filip FrÄ…cz

Wenn Sie .NET 4.0 verwenden, können Sie die Klasse System.Lazy nutzen . Dadurch wird sichergestellt, dass die Instanz nur einmal erstellt wird.

Sie sollten ein Codebeispiel hinzufügen, da dies meiner Meinung nach bei weitem der beste Weg ist, um ein Singleton mit .NET 4.0 zu implementieren. Scott Lerch vor 7 Jahren 5
Ein Codebeispiel ist unter http://csharpindepth.com/Articles/General/Singleton.aspx ("sechste Version") verfügbar. Konrad Morawski vor 6 Jahren 0
9
GWLlosa

Ich verwende ein Muster ähnlich einem bereits veröffentlichten, jedoch mit dem folgenden Unterschied:

public sealed class Logging
{
    static Logging instance = null;
    static readonly object lockObj = new object();

    private Logging()
    {
    }

    public static Logging Logger
    {
        get
        {
            **if (instance == null)**
            {
                 lock (lockObj)
                 {
                     if (instance == null)
                     {
                         instance = new Logging();
                     }

                 }
            }
            return instance;
        }
    }

}

Der Grund dafür ist, dass Anrufsperren jedes Mal vermieden werden, was Ihnen bei der Leistung helfen kann. Sie prüfen also auf null und sperren es, wenn es null ist. Dann müssen Sie noch einmal nach Null suchen (weil in dieser Sekunde jemand recht gekommen ist), aber dann sollten Sie in Ordnung sein. Auf diese Weise werden Sie nur beim ersten Mal (oder bei zwei) das Schloss treffen und an ihm vorbeiziehen, ohne die restliche Zeit blockieren zu müssen.

Tolles Beispiel. Ich muss nur den Namen hinzufügen: Er heißt "Double-checked Locking". Oleg Rybak vor 9 Jahren 0
Die return-Anweisung muss sich außerhalb der ersten if (instance == null) -Anweisung befinden. Andernfalls wird keine Instanz zurückgegeben, wenn die Instanz bereits vorhanden ist. vor 9 Jahren 1
@Dan ganz richtig, mein Fehler. GWLlosa vor 9 Jahren 0
6
RoflcoptrException

Ich verwende diese Lösung immer, wenn ich ein Singleton in C # implementieren muss.

public sealed class Logging
    {
        static Logging instance = null;
        static readonly object lockObj = new object();

        private Logging()
        {
        }

        public static Logging Logger
        {
            get
            {
                lock (lockObj)
                {
                    if (instance == null)
                    {
                        instance = new Logging();
                    }
                   return instance;
                }
            }
        }

}

Im Vergleich zu Ihrer Lösung ist dies threadsicher und verwendet eine Methode der lazy-creation. (Das Objekt wird nur instanziiert, wenn es tatsächlich benötigt wird)

Die eifrige vs. faule Kreation ist in diesem Beispiel nicht wirklich eine Diskussion wert, aber ich würde, wenn immer möglich, die Thread-sichere Implementierung verwenden.

Dies ist eine sehr schlechte Lösung für das Problem, da für jeden einzelnen Zugriff eine Sperre verwendet wird. Das hat einen sehr hohen Aufwand. Martin vor 9 Jahren 4
@Martin: Der Aufwand ist nicht so schwer, wenn Sie nicht jedes Mal `Logging.Logger.DoThis ()` und `Logging.Logger.DoThat ()` sagen. Das möchte ich sowieso lieber vermeiden, da (1) die Logging-Klasse ziemlich verschanzt wird, was es später noch schwieriger macht, gegen etwas anderes auszutauschen, und (2) `Logging.Logger` von der Definition eines Singleton, geben Sie immer dasselbe zurück, so dass es jedes Mal dumm ist, es wiederzuerlangen. Oh, und (3) es ist Wiederholung, und ich hasse Wiederholung. cHao vor 8 Jahren 0
Sie verwenden jetzt ein Singleton, aber später möchten Sie möglicherweise Ihr Singleton ändern. Es wird also beispielsweise zu einem neuen Logger ausgewechselt, wenn die Datei voll ist. Daher neige ich dazu, das Singleton-Caching nicht zu speichern. Es gibt keinen Sinn, hunderte von Verweisen auf etwas aufzubewahren, wenn Sie den Zugriff an einem Ort einfach und effizient z Martin vor 8 Jahren 0
4
codingoutloud

Abhängigkeit Injection-Container wie Unity unterstützen ein Einzelkonzept. Unity stammt von Microsoft (und ist Open Source), aber es gibt viele Open Source-DI-Container .

4

Hier ist ein Beispiel von der Seite "Überprüfte Sperren" von Wikipedia:

public class MySingleton
{
    private static object myLock = new object();
    private static MySingleton mySingleton = null;

    private MySingleton()
    { }

    public static MySingleton GetInstance()
    {
        if (mySingleton == null)    // check
        {
            lock (myLock)
            {
                if (mySingleton == null)    // double check
                {
                    MySingleton newSingleton = new MySingleton();
                    System.Threading.Thread.MemoryBarrier();
                    mySingleton = newSingleton;
                }
            }
        }

        return mySingleton;
    }
}

Beachten Sie die Verwendung von System.Threading.Thread.MemoryBarrier();, im Gegensatz zu GWLlosas Antwort. Weitere Informationen finden Sie in der Deklaration "Doppelte überprüfte Verriegelung ist unterbrochen" . (Es ist für Java geschrieben, aber es gelten die gleichen Prinzipien.)

4
Ufuk Hacıoğulları

Hier ist eine Implementierung, die verwendet Lazy<T>:

public class Foo : IFoo
{
    private static readonly Lazy<IFoo> __instance
        = new Lazy<IFoo>(valueFactory: () => new Foo(), isThreadSafe: true);

    private Foo() { }

    public static IFoo Instance { get { return __instance.Value; } }
}
0
paritosh

Einfache Möglichkeit, eine einfache Möglichkeit mit volatilen Schlüsselwörtern vorzuschlagen, statische Variablen sind nicht threadsicher, aber die atomare Ausführung wird als threadsicher betrachtet. Durch die Verwendung von flüchtigen Elementen können wir den atomaren Betrieb erzwingen.

public sealed class Singleton
{
    private static **volatile** readonly Singleton instance = new Singleton();
    public static Singleton Instance { get { return instance; } }
    private Singleton() { }
}

Das Sperren ist nicht der sichere Weg, da es teuer ist.

Ich mag John Skeets Art, die Singleton-Art der Objekte zu erstellen.