Синглтон от Jon Skeet разъяснения - PullRequest
212 голосов
/ 31 марта 2010
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();
    }
}

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

У меня два сомнения по поводу кода

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

    internal static readonly Singleton instance = new Singleton();
    

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

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

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

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

Ответы [ 2 ]

353 голосов
/ 31 марта 2010
  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() {}
}
48 голосов
/ 18 января 2013

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

    private class Nested

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

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

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

Это знакомит с постом Джона о singleton , который является IMO основной темой этого вопроса.Да, и сомнения: -)

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

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

...