Реализация шаблона Singleton в C #

Это лучший способ реализовать этот шаблон в C #?

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

    static Singleton() {}
    private Singleton() {}
}
67 голосов | спросил Aim Kai 20 Jpm1000000pmThu, 20 Jan 2011 13:02:03 +030011 2011, 13:02:03

9 ответов


75

Я использую версию Jon Skeet потокового безопасного синглтона с полностью ленивым экземпляром в C #:

public sealed class Singleton
{
    // Thread safe Singleton with fully lazy instantiation á la Jon Skeet:
    // http://csharpindepth.com/Articles/General/Singleton.aspx
    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();
    }
}

Это творит чудеса для меня! Это очень просто в использовании, просто получите экземпляр из свойства Instance; SingletonName instance = SingletonName.Instance;

ответил Zolomon 20 Jpm1000000pmThu, 20 Jan 2011 13:15:28 +030011 2011, 13:15:28
18

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

public static class Singleton<T> where T : class, new()
{
    private T instance = null;

    public T Instance
    {
        get
        {
            if (instance == null)
                Interlocked.CompareExchange(ref instance, new T(), null);

            return instance;
        }
    }
}

Если вы не знакомы с блокированным классом, он выполняет атомарные операции таким образом, который часто быстрее, чем блокировка. Три возможных случая:

1) Первый доступ одним потоком . Вероятно, примерно такая же производительность, как и очевидный метод с использованием блокировки

2) Первый доступ по многим темам одновременно . Многие потоки могут войти в блокированный обмен, и в этом случае можно создать несколько элементов, но только 1 будет «выигрывать». Пока ваш конструктор не имеет глобальных побочных эффектов (что действительно не должно), поведение будет правильным. Производительность будет немного меньше блокировки из-за множества распределений, но накладные расходы небольшие, и это очень редкий случай.

3) Позднее вызывается . Никаких операций блокировки или блокировки, это довольно оптимально и, очевидно, является большинством.

ответил Martin 20 Jpm1000000pmThu, 20 Jan 2011 15:55:00 +030011 2011, 15:55:00
13

Если вы используете .NET 4.0, вы можете воспользоваться System.Lazy класс. Он гарантирует, что экземпляр создается только один раз.

ответил Filip Frącz 27 Jpm1000000pmThu, 27 Jan 2011 19:07:20 +030011 2011, 19:07:20
9

Я использую шаблон, аналогичный уже опубликованному, но со следующей разницей:

public sealed class Logging
{
    static Logging instance = null;
    static readonly object lockObj = new object();

    private Logging()
    {
    }

    public static Logging Logger
    {
        get
        {
            **if (instance == null)**
            {
                 lock (lockObj)
                 {
                     if (instance == null)
                     {
                         instance = new Logging();
                     }

                 }
            }
            return instance;
        }
    }

}

Причина в том, что он каждый раз избегает блокировки вызовов, что может помочь вам в производительности. Таким образом, вы проверяете значение null, а затем, если оно равно null, заблокируйте его. Затем вам нужно снова проверить значение null (потому что кто-то, возможно, пришел в эту секунду), но тогда вы должны быть в порядке. Таким образом, вы впервые нажмете блокировку в первый раз (или два) и пропустите его, не блокируя оставшуюся часть времени.

ответил GWLlosa 21 Jam1000000amFri, 21 Jan 2011 06:57:08 +030011 2011, 06:57:08
6

Я всегда использую это решение, если мне нужно реализовать Singleton в C #

public sealed class Logging
    {
        static Logging instance = null;
        static readonly object lockObj = new object();

        private Logging()
        {
        }

        public static Logging Logger
        {
            get
            {
                lock (lockObj)
                {
                    if (instance == null)
                    {
                        instance = new Logging();
                    }
                   return instance;
                }
            }
        }

}

По сравнению с вашим решением это потокобезопасно и использует метод ленивого создания. (Объект создается только при необходимости)

Желание против ленивого создания на самом деле не стоит обсуждать в этом примере, но я по возможности использовал бы потокобезопасную реализацию.

ответил RoflcoptrException 20 Jpm1000000pmThu, 20 Jan 2011 13:07:05 +030011 2011, 13:07:05
4

Зависимость Контейнеры для инъекций, такие как Unity, поддерживают концепцию singleton. Unity от Microsoft (и с открытым исходным кодом), но есть много контейнеров DI с открытым исходным кодом .

ответил codingoutloud 27 Jam1000000amThu, 27 Jan 2011 02:58:53 +030011 2011, 02:58:53
4

Вот пример из страницы блокировки с двойной проверкой Wikipedia:

public class MySingleton
{
    private static object myLock = new object();
    private static MySingleton mySingleton = null;

    private MySingleton()
    { }

    public static MySingleton GetInstance()
    {
        if (mySingleton == null)    // check
        {
            lock (myLock)
            {
                if (mySingleton == null)    // double check
                {
                    MySingleton newSingleton = new MySingleton();
                    System.Threading.Thread.MemoryBarrier();
                    mySingleton = newSingleton;
                }
            }
        }

        return mySingleton;
    }
}

Обратите внимание на использование System.Threading.Thread.MemoryBarrier();, в отличие от ответа GWLlosa. См. Объявление с двойной проверкой блокировки для объяснение. (Он написан для Java, но применяются те же принципы.)

ответил 27 Jpm1000000pmThu, 27 Jan 2011 22:22:58 +030011 2011, 22:22:58
4

Вот реализация, которая использует Lazy<T> :

public class Foo : IFoo
{
    private static readonly Lazy<IFoo> __instance
        = new Lazy<IFoo>(valueFactory: () => new Foo(), isThreadSafe: true);

    private Foo() { }

    public static IFoo Instance { get { return __instance.Value; } }
}
ответил Ufuk Hacıoğulları 10 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowMon, 10 Sep 2012 16:10:32 +0400 2012, 16:10:32
0

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

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

Блокировка - это не верный путь, так как это дорого.

Мне нравится john skeet способ создания одноэлементного способа создания объектов.

ответил paritosh 21 FriEurope/Moscow2012-12-21T20:21:55+04:00Europe/Moscow12bEurope/MoscowFri, 21 Dec 2012 20:21:55 +0400 2012, 20:21:55

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132