Синглтон, поясняющий Джон Скит

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

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

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

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

    internal static readonly Singleton instance = new Singleton();
    

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

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

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

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

Ответ 1

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

  • Прочитайте статью на странице 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() {}
}

Ответ 2

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

    private class Nested

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

Итак, если вы не хотите, чтобы среда выполнения инициализировала "проактивно" поля, прежде чем использовать их, добавьте статический конструктор.

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

Это приносит сообщение Джону о singleton, что является ИМО основной темой этого вопроса. Ох и сомнения: -)

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

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