Синглтон от 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();
    }
}

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

У меня есть два сомнения в коде

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

    internal static readonly Singleton instance = new Singleton();

    Что-то называется закрытием?

  2. Я не могу понять этот комментарий

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

    что этот комментарий предлагает нам?

amutha
источник
12
ха-ха, я думал, что сказал, что это немного беспокоит лол ... оказался другой Джон Нолан
Джон Энтони Даниэль Нолан
14
Две вещи общеприняты: солнце встает с востока и Джон Скит всегда прав. Но я еще не уверен насчет первого: P
akhil_mittal
2
@ thepirat000 - Если бы он был только участником SO / Meta, я мог бы не согласиться, но он имеет достаточно влияния в реальном мире программирования, чтобы это могло быть вполне законным - я уверен, что кто-то создал его в тот или иной момент ,
Код жокея
8
Таксономия этого вопроса обсуждается на мета .
BoltClock

Ответы:

359
  1. Нет, это не имеет ничего общего с замыканиями. Вложенный класс имеет доступ к закрытым членам своего внешнего класса, включая приватный конструктор.

  2. Прочитайте мою статью на beforefieldinit . Вы можете или не можете хотеть статический конструктор no-op - это зависит от того, что лень гарантирует вам нужно. Вы должны знать, что .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 по электронной почте, чтобы пожаловаться :)
Джон Скит
2
@JonSkeet, я только что выразил обеспокоенность JetBrains по этому поводу (# RSRP-274373). Давайте посмотрим, что они могут придумать. :)
Анинда Чаттерджи
3
@ Лун: Ты не. Синглтон живет в течение срока действия домена приложения.
Джон Скит
2
@JonSkeet Любую причину не использовать, Lazy<T>чтобы вам не приходилось объявлять статический конструктор для магического BeforeFieldInitпобочного эффекта?
Эд Т
3
FieldBeforeInitэто MahaBharataизMicrosoft
Амит Кумар Гхош
49

Относительно вопроса (1): ответ от Джона верен, поскольку он неявно помечает класс как «Вложенный» как закрытый, не делая его открытым или внутренним :-). Вы также можете сделать это явно, добавив «private»:

    private class Nested

Относительно вопроса (2): в основном, что говорится в посте beforeinitfield и инициализации типов , что если у вас нет статического конструктора, среда выполнения может инициализировать его в любое время (но до его использования). Если у вас есть статический конструктор, ваш код в статическом конструкторе может инициализировать поля, что означает, что среде выполнения разрешено инициализировать поле только при запросе типа.

Поэтому, если вы не хотите, чтобы среда выполнения инициализировала поля «проактивно» перед их использованием, добавьте статический конструктор.

В любом случае, если вы реализуете синглтоны, вы хотите, чтобы он инициализировался как можно более лениво, а не тогда, когда среда выполнения думает, что должна инициализировать вашу переменную - или вам, вероятно, просто все равно. От вашего вопроса, я полагаю, вы хотите их как можно позже.

Это приводит к встрече Джона о синглтоне , который является основной темой этого вопроса. Ох и сомнения :-)

Я хотел бы отметить, что его синглтон № 3, который он пометил как «неправильный», на самом деле правильный (потому что блокировка автоматически подразумевает барьер памяти при выходе ). Он также должен быть быстрее, чем синглтон # 2, когда вы используете экземпляр более одного раза (что более или менее является точкой синглтона :-)). Так что, если вам действительно нужна ленивая одноэлементная реализация, я бы, вероятно, пошел на это - по тем простым причинам, что (1) всем ясно, что читает ваш код, что происходит, и (2) вы знаете, что произойдет за исключением

Если вам интересно: я бы никогда не использовал синглтон # 6, потому что это может легко привести к тупикам и неожиданному поведению с исключениями. Для получения дополнительной информации см .: режим блокировки lazy , в частности, ExecutionAndPublication.

atlaste
источник
62
Regarding question (1): The answer from Jon is correct ...Джон Скит всегда прав ....
Noctis
72
Дополнительные баллы за попытку ответить на вопрос Джона Скита, на который Джон Скит уже ответил.
Вальдетеро
8
@valdetero Хахаха. Это ... хахаха +1
akinuri