Каков хороший шаблонный шаблонный шаблон в C # - PullRequest
30 голосов
/ 19 сентября 2008

У меня есть следующий шаблон C # singleton, есть ли способ улучшить его?

    public class Singleton<T> where T : class, new()
    {

        private static object _syncobj = new object();
        private static volatile T _instance = null;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncobj)
                    {
                        if (_instance == null)
                        {
                            _instance = new T();
                        }
                    }
                }
                return _instance;
            }
        }

        public Singleton()
        { }

    }

Пример предпочтительного использования:

class Foo : Singleton<Foo> 
{
} 

Связано :

Очевидная одноэлементная реализация для .NET?

Ответы [ 22 ]

17 голосов
/ 19 сентября 2008

Согласно Джону Скиту (Jon Skeet) из Реализация шаблона Singleton в C # код, который вы опубликовали, на самом деле считается плохим кодом, потому что при проверке по стандарту CLI ECMA он выглядит неработающим.

Также следите: каждый раз, когда вы создаете экземпляр своего объекта с новым типом T, он становится другим экземпляром; это не отражается на вашем оригинальном синглтоне.

9 голосов
/ 19 сентября 2008

Этот код не будет компилироваться, вам нужно ограничение "class" для T.

Кроме того, для этого кода требуется открытый конструктор целевого класса, что не подходит для синглтона, поскольку во время компиляции вы не можете управлять тем, что получаете (одиночный) экземпляр только через свойство (или поле) экземпляра. Если у вас нет других статических членов, кроме Instance, все в порядке с этим:

class Foo
{
  public static readonly Instance = new Foo();
  private Foo() {}
  static Foo() {}
}

Это потокобезопасный (гарантируется CLR) и ленивый (экземпляр создается с первым доступом к типу). Для получения дополнительной информации о BeforeFieldInit и почему нам нужен статический конструктор, см. https://csharpindepth.com/articles/BeforeFieldInit.

Если вы хотите иметь другие открытые статические члены для типа, но создавать объект только при доступе к экземпляру, вы можете создать вложенный тип, как в https://csharpindepth.com/articles/Singleton

9 голосов
/ 19 сентября 2008

Предоставлено Джудит Бишоп, http://patterns.cs.up.ac.za/

Эта одноэлементная реализация шаблона обеспечивает отложенную инициализацию.

//  Singleton PatternJudith Bishop Nov 2007
//  Generic version

public class Singleton<T> where T : class, new()
{
    Singleton() { }

    class SingletonCreator
    {
        static SingletonCreator() { }
        // Private object instantiated with private constructor
        internal static readonly T instance = new T();
    }

    public static T UniqueInstance
    {
        get { return SingletonCreator.instance; }
    }
}
6 голосов
/ 25 октября 2011

Это моя точка зрения с использованием .NET 4

public class Singleton<T> where T : class, new()
    {
        Singleton (){}

        private static readonly Lazy<T> instance = new Lazy<T>(()=> new T());

        public static T Instance { get { return instance.Value; } } 
    }
3 голосов
/ 11 декабря 2008

Подробнее об этом ответе в другой теме: Как реализовать синглтон в C #?

Однако поток не использует универсальный .

2 голосов
/ 15 ноября 2008

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

Всякий раз, когда вам нужен синглтон, просто сделайте это:

class MyConcreteClass
{
  #region Singleton Implementation

    public static readonly Instance = new MyConcreteClass();

    private MyConcreteClass(){}

  #endregion

    /// ...
}
1 голос
/ 18 июня 2009

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

Моя версия использует Reflection, работает с непубличными конструкторами в производном классе, является поточно-ориентированной (очевидно) с ленивым созданием (согласно статье, которую я нашел связанной ниже):

public class SingletonBase<T> where T : class
{
    static SingletonBase()
    {
    }

    public static readonly T Instance = 
        typeof(T).InvokeMember(typeof(T).Name, 
                                BindingFlags.CreateInstance | 
                                BindingFlags.Instance |
                                BindingFlags.Public |
                                BindingFlags.NonPublic, 
                                null, null, null) as T;
}

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

Это самый старый источник кода, который я могу найти , это не я его отправлял.

1 голос
/ 20 июля 2011

Идиома Double-Check Locking [Lea99], предоставленная Microsoft здесь , удивительно похожа на предоставленный вами код, к сожалению, это не соответствует стандарту CLI ECMA для пуританского взгляда на потокобезопасный код и может не работать правильно во всех ситуациях.

В многопоточной программе разные потоки могут пытаться создать экземпляр класса одновременно. По этой причине реализация Singleton, которая полагается на оператор if, чтобы проверить, является ли экземпляр пустым , не будет поточно-безопасным . Не пишите такой код!

Простой, но эффективный способ создания поточно-ориентированного синглтона - это использовать вложенный класс для его создания. Ниже приведен пример ленивого экземпляра синглтона:

public sealed class Singleton
{ 
   private Singleton() { }

   public static Singleton Instance
   {
      get
      {
         return SingletonCreator.instance;
      }
   }

   private class SingletonCreator
   {
      static SingletonCreator() { }
      internal static readonly Singleton instance = new Singleton();
   }
}

Использование:

Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Singleton objects are the same");
}

Универсальное решение:

public class Singleton<T>
    where T : class, new()
{
    private Singleton() { }

    public static T Instance 
    { 
        get 
        { 
            return SingletonCreator.instance; 
        } 
    } 

    private class SingletonCreator 
    {
        static SingletonCreator() { }

        internal static readonly T instance = new T();
    }
}

Использование:

class TestClass { }

Singleton s1 = Singleton<TestClass>.Instance;
Singleton s2 = Singleton<TestClass>.Instance;
if (s1.Equals(s2))
{
    Console.WriteLine("Thread-Safe Generic Singleton objects are the same");
}

Наконец, вот несколько выпущенное и полезное предложение - чтобы избежать взаимных блокировок, которые могут быть вызваны использованием ключевого слова lock, рассмотрите возможность добавления следующего атрибута для защиты кода только в открытых статических методах:

using System.Runtime.CompilerServices;
[MethodImpl (MethodImplOptions.Synchronized)]
public static void MySynchronizedMethod()
{
}

Ссылка:

  1. Кулинарная книга C # (О'Рейли), Джей Хилярд и Стивен Тейлет
  2. Шаблон дизайна C # 3.0 (О'Рейли), Джудит Бишоп
  3. CSharp-Online.Net - Шаблон проектирования Singleton: Потокобезопасный Singleton
1 голос
/ 24 августа 2009

: / общий шаблон "синглтон" Джудит-епископа кажется несколько ошибочным, всегда можно создать несколько экземпляров типа T, так как конструктор должен быть публичным, чтобы использовать его в этом "шаблоне". На мой взгляд, это не имеет ничего общего с синглтоном, это просто своего рода фабрика, которая всегда возвращает один и тот же объект, но не делает его синглтоном ... до тех пор, пока может быть более 1 экземпляра класса. не может быть одинокой По какой причине этот шаблон имеет самый высокий рейтинг?

public sealed class Singleton
{
  private static readonly Singleton _instance = new Singleton();

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return _instance;
    }
  }
}

статические инициализаторы считаются поточно-безопасными. Я не знаю, но вам вообще не следует использовать идиомы синглтона, если вы переносите мой код выше его не более чем на 3 строки ... и наследование от синглтона не Не имеет никакого смысла либо

1 голос
/ 20 октября 2009

Попробуйте этот универсальный класс Singleton, реализующий шаблон проектирования Singleton безопасным и ленивым способом (thx to wcell).

public abstract class Singleton<T> where T : class
{
    /// <summary>
    /// Returns the singleton instance.
    /// </summary>
    public static T Instance
    {
        get
        {
            return SingletonAllocator.instance;
        }
    }

    internal static class SingletonAllocator
    {
        internal static T instance;

        static SingletonAllocator()
        {
            CreateInstance(typeof(T));
        }

        public static T CreateInstance(Type type)
        {
            ConstructorInfo[] ctorsPublic = type.GetConstructors(
                BindingFlags.Instance | BindingFlags.Public);

            if (ctorsPublic.Length > 0)
                throw new Exception(
                    type.FullName + " has one or more public constructors so the property cannot be enforced.");

            ConstructorInfo ctorNonPublic = type.GetConstructor(
                BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], new ParameterModifier[0]);

            if (ctorNonPublic == null)
            {
                throw new Exception(
                    type.FullName + " doesn't have a private/protected constructor so the property cannot be enforced.");
            }

            try
            {
                return instance = (T)ctorNonPublic.Invoke(new object[0]);
            }
            catch (Exception e)
            {
                throw new Exception(
                    "The Singleton couldnt be constructed, check if " + type.FullName + " has a default constructor", e);
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...