web-dev-qa-db-fra.com

Singleton par Jon Skeet clarification

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

Je souhaite implémenter motif Singleton de Jon Skeet dans mon application actuelle en C #.

J'ai deux doutes sur le code

  1. Comment est-il possible d'accéder à la classe externe à l'intérieur de la classe imbriquée? je veux dire

    internal static readonly Singleton instance = new Singleton();
    

    Est-ce que quelque chose s'appelle la fermeture?

  2. Je suis incapable de comprendre ce commentaire

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

    que nous suggère ce commentaire?

211
amutha
  1. Non, cela n'a rien à voir avec les fermetures. Une classe imbriquée a accès aux membres privés de sa classe externe, y compris le constructeur privé ici.

  2. Lisez mon article sur beforefieldinit . Vous pouvez ou non vouloir le constructeur statique no-op - cela dépend de ce que la paresse vous garantit. Vous devez savoir que .NET 4 modifie quelque peu la sémantique d'initialisation de type réelle (toujours dans les spécifications, mais plus paresseux qu'auparavant ).

Avez-vous vraiment ce modèle? Êtes-vous sûr de ne pas pouvoir vous en sortir:

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

    static Singleton() {}
    private Singleton() {}
}
354
Jon Skeet

Concernant la question (1): la réponse de Jon est correcte car il marque implicitement la classe 'imbriquée' comme privée en ne la rendant pas publique ou interne :-). Vous pouvez aussi le faire explicitement en ajoutant 'private':

    private class Nested

Concernant la question (2): quoi que le post sur beforeinitfield et initialisation du type indique que si vous n’avez pas de constructeur statique, le moteur d’exécution peut l’initialiser à tout moment (mais avant de l’utiliser). Si vous avez un constructeur statique, votre code dans le constructeur statique peut initialiser les champs, ce qui signifie que le moteur d'exécution n'est autorisé à initialiser le champ que lorsque vous demandez le type.

Donc, si vous ne voulez pas que le moteur d’initialisation initialise les champs de manière "proactive" avant de les utiliser, ajoutez un constructeur statique.

Quoi qu’il en soit, si vous implémentez des singletons, vous voulez que le processus d’initialisation soit le plus lent possible, et non pas lorsque le moteur d’exécution le juge bien, il faut initialiser votre variable - ou vous ne vous en souciez probablement pas. D'après votre question, je suppose que vous les voulez le plus tard possible.

Cela nous amène à répondre au message de Jon sur singleton , qui est le sujet sous-jacent de cette question à l’OMI. Oh et les doutes :-)

J'aimerais souligner que son singleton # 3, qu'il a marqué "faux", est en fait correct (car le verrou est automatiquement implique une barrière de mémoire à la sortie ). Il devrait également être plus rapide que singleton # 2 lorsque vous utilisez l'instance plusieurs fois (ce qui est plus ou moins le point d'un singleton :-)). Donc, si vous avez vraiment besoin d'une implémentation singleton paresseuse, je préférerais probablement celle-ci - pour des raisons simples: (1) il est très clair pour tout le monde qui lit votre code ce qui se passe et (2) vous savez ce qui va arriver avec des exceptions.

Au cas où vous vous le demanderiez: je n'utiliserais jamais le singleton # 6 car cela peut facilement conduire à des blocages et à un comportement inattendu avec des exceptions. Pour plus de détails, voir: mode de verrouillage de lazy , plus précisément ExecutionAndPublication.

48
atlaste