Clarification de Singleton par Jon Skeet


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

Je souhaite implémenter le modèle 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 quelque chose appelé fermeture?

  2. Je n'arrive pas à comprendre ce commentaire

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

    que nous suggère ce commentaire?


12
haha je pensais avoir dit que c'était un peu inquiet lol ... s'est avéré être un autre John Nolan
John Antony Daniel Nolan

14
Deux choses sont universellement acceptées: le soleil se lève de l'est et Jon Skeet a toujours raison. Mais je ne suis pas encore sûr de l'ancien: P
akhil_mittal

2
@ thepirat000 - S'il n'était qu'un participant sur SO / Meta, je suis peut-être en désaccord, mais il a suffisamment d'influence dans le monde réel de la programmation pour que cela soit réellement légitime - je suis sûr que quelqu'un l'a créé à un moment ou à un autre .
Code Jockey

8
La taxonomie de cette question est en cours de discussion sur les méta .
BoltClock

Réponses:


359
  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 sans opération - cela dépend de ce que la paresse garantit dont vous avez besoin. Vous devez savoir que .NET 4 modifie quelque peu la sémantique d'initialisation du type réel (toujours dans la spécification, mais plus paresseux qu'auparavant).

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

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

    static Singleton() {}
    private Singleton() {}
}

12
@Anindya: Non, ça va. Vous voudrez peut-être envoyer un mail à JetBrains pour vous plaindre :)
Jon Skeet

2
@ JonSkeet, je viens de soulever une préoccupation à JetBrains à ce sujet (# RSRP-274373). Voyons voir ce qu'ils peuvent trouver. :)
Anindya Chatterjee

3
@Moons: Non. Un singleton vit pendant la durée de l'AppDomain.
Jon Skeet

2
@JonSkeet Y a-t-il une raison de ne pas l'utiliser Lazy<T>pour que vous n'ayez pas à déclarer un constructeur statique pour l' BeforeFieldIniteffet secondaire magique ?
Ed T

3
FieldBeforeInitvient MahaBharatadeMicrosoft
Amit Kumar Ghosh

49

Concernant la question (1): La réponse de Jon est correcte, car il marque implicitement la classe 'Nested' privé en ne la rendant pas publique ou interne :-). Vous pourriez aussi bien le faire explicitement en ajoutant «privé»:

    private class Nested

Concernant la question (2): fondamentalement, ce que la publication sur beforeinitfield et l' initialisation de type vous dit, c'est que si vous n'avez pas de constructeur statique, le runtime 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 runtime n'est autorisé à initialiser le champ que lorsque vous demandez le type.

Donc, si vous ne voulez pas que le runtime initialise les champs «proactivement» avant de les utiliser, ajoutez un constructeur statique.

Quoi qu'il en soit, si vous implémentez des singletons, vous voulez soit l'initialiser aussi paresseusement que possible et non pas lorsque le runtime pense qu'il devrait 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 amène à la publication de Jon sur le singleton , qui est l'OMI le sujet sous-jacent de cette question. Oh et les doutes :-)

Je voudrais souligner que son singleton # 3, qu'il a marqué `` faux '', est en fait correct (car le verrouillage implique automatiquement 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 de singleton paresseux, j'irais probablement pour celle-ci - pour les raisons simples que (1) c'est très clair pour tout le monde qui lit votre code ce qui se passe et (2) vous savez ce qui se passera avec des exceptions.

Au cas où vous vous poseriez la question: 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: le mode de verrouillage de lazy , en particulier ExecutionAndPublication.


62
Regarding question (1): The answer from Jon is correct ...Jon Skeet a toujours raison ....
Noctis

72
Points supplémentaires pour tenter de répondre à une question de Jon Skeet à laquelle Jon Skeet a déjà répondu.
valdetero

8
@valdetero Hahaha. Ce ... hahaha +1
akinuri
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.