Сінглтон від уточнення Джона Скіта


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

Я хочу реалізувати шаблон Singleton Джона Скіта в моєму теперішньому застосуванні в C #.

У мене є два сумніви щодо коду

  1. Як можна отримати доступ до зовнішнього класу всередині вкладеного класу? я маю на увазі

    internal static readonly Singleton instance = new Singleton();

    Щось називається закриттям?

  2. Я не можу зрозуміти цей коментар

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

    що нам пропонує цей коментар?


12
ха-ха, я думав, що сказав, що трохи хвилювався хаха ... виявився інший Джон Нолан
Джон Ентоні Даніель Нолан

14
Загальновизнані дві речі: сонце сходить зі сходу, і Джон Скіт завжди правий. Але я ще не впевнений у колишньому: P
akhil_mittal

2
@ thepirat000 - Якби він був лише учасником SO / Meta, я, можливо, не погоджуюся, але він має достатній вплив у реальному світі програмування, що це насправді може бути законним - я впевнений, що хтось створив це в той чи інший момент .
Кодекс Жокі

8
Таксономія цього питання обговорюється на мета .
BoltClock

Відповіді:


359
  1. Ні, це не має нічого спільного із закриттями. Вкладений клас має доступ до приватних членів свого зовнішнього класу, включаючи тут приватний конструктор.

  2. Прочитайте мою статтю про предіфілініт . Ви можете або не захочете статичного конструктора без використання - це залежить від того, які лінощі гарантують вам необхідність. Ви повинні знати, що .NET 4 дещо змінює семантику ініціалізації фактичного типу (все ще в межах специфікації, але лазурше, ніж раніше).

Вам справді потрібен цей візерунок? Ви впевнені, що не можете втекти:

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

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

12
@Anindya: Ні, це добре. Можливо, ви хочете надіслати повідомлення JetBrains, щоб скаржитися :)
Jon Skeet

2
@JonSkeet, я щойно викликав стурбованість JetBrains з цього приводу (# RSRP-274373). Давайте подивимось, що вони можуть придумати. :)
Anindya Chatterjee

3
@Moons: Ні. Одинокий живе протягом тривалості AppDomain.
Джон Скіт

2
@JonSkeet Будь-яка причина не використовувати Lazy<T>так, що вам не доведеться оголошувати статичний конструктор для магічного BeforeFieldInitпобічного ефекту?
Ед Т

3
FieldBeforeInitis MahaBharatafromMicrosoft
Amit Kumar Ghosh

49

Щодо питання (1): Відповідь Джона є правильною, оскільки він неявно позначає клас "Вкладений" приватним, не роблячи його публічним чи внутрішнім :-). Ви також можете це зробити прямо, додавши "приватне":

    private class Nested

Щодо питання (2): в основному те, про що розповідає публікація про ініціалізацію перед типом і типом, це те, що якщо у вас немає статичного конструктора, час виконання може ініціалізувати його в будь-який час (але перед тим, як використовувати його). Якщо у вас є статичний конструктор, ваш код у статичному конструкторі може ініціалізувати поля, а це означає, що час виконання дозволено ініціалізувати поле лише тоді, коли ви запитаєте тип.

Тож якщо ви не хочете, щоб час виконання програми ініціалізував поля "проактивно" перед їх використанням, додайте статичний конструктор.

У будь-якому випадку, якщо ви реалізовуєте одиночні кнопки, ви хочете, щоб він ініціалізувався якомога лініше, а не тоді, коли під час виконання програми вважає, що він повинен ініціалізувати вашу змінну - або вам, мабуть, просто все одно. З вашого запитання, я думаю, ви хочете їх якомога пізніше.

Це знайшло зустріч з повідомленням Джона про одиночного , що є основним темою цього питання в ІМО. Ох і сумніви :-)

Я хотів би зазначити, що його одиночний номер 3, який він позначав "неправильним", насправді правильний (оскільки замок автоматично передбачає бар'єр пам'яті на виході ). Це також повинно бути швидшим, ніж сингл # 2, коли ви використовуєте екземпляр більше одного разу (що є більш-менш точкою одинарного :-)). Отже, якщо вам справді потрібна лінива реалізація синглтона, я, мабуть, пішов би саме з цієї причини - з простих причин, що (1) для всіх, хто читає ваш код, зрозуміло, що відбувається, і (2) ви знаєте, що буде за винятком.

У випадку, якщо вам цікаво: я б ніколи не використовував одиночний номер 6, оскільки це може призвести до тупиків та несподіваної поведінки за винятками. Докладніше див. У режимі блокування ледачих , зокрема ExecutionAndPublication.


62
Regarding question (1): The answer from Jon is correct ...Джон Скіт завжди правильний ....
Noctis

72
Додаткові бали за спробу відповіді на питання про Джон Скіта, на яке Джон Скіт вже відповів.
valdetero

8
@valdetero Hahaha. Це ... хахаха +1
akinuri
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.