Singleton von Jon Skeet Klarstellung

214
public sealed class Singleton
{
    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();
    }
}

Ich möchte das Singleton-Muster von Jon Skeet in meiner aktuellen Anwendung in C # implementieren .

Ich habe zwei Zweifel am Code

  1. Wie ist es möglich, auf die äußere Klasse innerhalb einer verschachtelten Klasse zuzugreifen? ich meine

    internal static readonly Singleton instance = new Singleton();

    Wird so etwas als Schließung bezeichnet?

  2. Ich kann diesen Kommentar nicht verstehen

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    

    Was schlägt uns dieser Kommentar vor?

Amutha
quelle
12
haha ich dachte ich hätte gesagt das wäre ein bisschen besorgt lol ... stellte sich als ein anderer heraus John Nolan
John Antony Daniel Nolan
14
Zwei Dinge werden allgemein akzeptiert: Die Sonne geht von Osten auf und Jon Skeet hat immer Recht. Aber ich bin mir noch nicht sicher über das erstere: P
akhil_mittal
2
@ thepirat000 - Wenn er nur ein Teilnehmer an SO / Meta wäre, könnte ich nicht zustimmen, aber er hat genug Einfluss auf die tatsächliche Welt der Programmierung, dass dies tatsächlich legitim sein könnte - ich bin sicher, dass jemand es irgendwann erstellt hat .
Code Jockey
8
Die Taxonomie dieser Frage wird auf Meta diskutiert .
BoltClock

Antworten:

359
  1. Nein, das hat nichts mit Verschlüssen zu tun. Eine verschachtelte Klasse hat Zugriff auf die privaten Mitglieder ihrer äußeren Klasse, einschließlich des privaten Konstruktors hier.

  2. Lesen Sie meinen Artikel auf beforefieldinit . Möglicherweise möchten Sie den statischen No-Op-Konstruktor oder nicht - dies hängt davon ab, welche Faulheit Sie benötigen. Sie sollten sich bewusst sein, dass .NET 4 die tatsächliche Semantik der Typinitialisierung etwas ändert (immer noch innerhalb der Spezifikation, aber fauler als zuvor).

Benötigen Sie dieses Muster wirklich ? Sind Sie sicher, dass Sie nicht durchkommen können mit:

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

    static Singleton() {}
    private Singleton() {}
}
Jon Skeet
quelle
12
@ Anindya: Nein, das ist in Ordnung. Vielleicht möchten Sie JetBrains mailen, um sich zu beschweren :)
Jon Skeet
2
@ JonSkeet, ich habe JetBrains gerade darüber Bedenken geäußert (# RSRP-274373). Mal sehen, was sie sich einfallen lassen können. :)
Anindya Chatterjee
3
@Moons: Das tust du nicht. Ein Singleton lebt für die Dauer der AppDomain.
Jon Skeet
2
@JonSkeet Gibt es einen Grund, nicht zu verwenden, Lazy<T>damit Sie keinen statischen Konstruktor für den magischen BeforeFieldInitNebeneffekt deklarieren müssen ?
Ed T
3
FieldBeforeInitist MahaBharatavonMicrosoft
Amit Kumar Ghosh
49

Zu Frage (1): Die Antwort von Jon ist richtig, da er die Klasse 'Verschachtelt' implizit als privat markiert, indem er sie nicht öffentlich oder intern macht :-). Sie können dies auch explizit tun, indem Sie 'privat' hinzufügen:

    private class Nested

Zu Frage (2): im Grunde , was die Post über beforeinitfield und Typ Initialisierung Sie sagen , ist , dass , wenn Sie keine statische Konstruktor haben, kann die Laufzeit es jederzeit initialisiert werden (aber , bevor Sie es verwenden). Wenn Sie einen statischen Konstruktor haben, initialisiert Ihr Code im statischen Konstruktor möglicherweise die Felder. Dies bedeutet, dass die Laufzeit das Feld nur initialisieren darf, wenn Sie nach dem Typ fragen.

Wenn Sie also nicht möchten, dass die Laufzeitfelder "proaktiv" initialisiert, bevor Sie sie verwenden, fügen Sie einen statischen Konstruktor hinzu.

So oder so, wenn Sie Singletons implementieren, möchten Sie entweder, dass es so faul wie möglich initialisiert wird und nicht, wenn die Laufzeit denkt, dass es Ihre Variable initialisieren sollte - oder es ist Ihnen wahrscheinlich einfach egal. Aufgrund Ihrer Frage möchten Sie sie vermutlich so spät wie möglich.

Das bringt Jons Beitrag über Singletons auf den Punkt, der IMO das zugrunde liegende Thema dieser Frage ist. Oh und die Zweifel :-)

Ich möchte darauf hinweisen, dass sein Singleton # 3, den er als "falsch" markiert hat, tatsächlich korrekt ist (da Sperren beim Beenden automatisch eine Speicherbarriere implizieren ). Es sollte auch schneller als Singleton # 2 sein, wenn Sie die Instanz mehr als einmal verwenden (was mehr oder weniger der Sinn eines Singletons ist :-)). Wenn Sie also wirklich eine faule Singleton-Implementierung benötigen, würde ich mich wahrscheinlich für diese entscheiden - aus den einfachen Gründen, dass (1) es für jeden, der Ihren Code liest, sehr klar ist, was los ist, und (2) Sie wissen, was passieren wird mit Ausnahmen.

Falls Sie sich fragen: Ich würde niemals Singleton Nr. 6 verwenden, da dies mit Ausnahmen leicht zu Deadlocks und unerwartetem Verhalten führen kann. Weitere Informationen finden Sie unter: Lazys Sperrmodus , insbesondere ExecutionAndPublication.

atlaste
quelle
62
Regarding question (1): The answer from Jon is correct ...Jon Skeet hat immer Recht ...
Noctis
72
Zusätzliche Punkte für den Versuch einer Antwort auf eine Jon Skeet-Frage, auf die Jon Skeet bereits geantwortet hat.
valdetero
8
@valdetero Hahaha. Dies ... hahaha +1
akinuri