Подтвердить что ты не робот

Какой хороший шаблонный шаблон шаблона 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> 
{
} 

Связанный:

Очевидная реализация singleton для .NET?

4b9b3361

Ответ 1

По словам Джона Скита из Реализация шаблона Singleton в С# the code you posted is actually considered as bad code, because it appears broken when checked against the ECMA CLI standard.

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

Ответ 2

Предоставлено Джудит Бишоп, 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; }
    }
}

Ответ 3

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

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

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

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

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

Ответ 4

Это моя точка, использующая .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; } } 
    }

Ответ 6

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

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

class MyConcreteClass
{
  #region Singleton Implementation

    public static readonly Instance = new MyConcreteClass();

    private MyConcreteClass(){}

  #endregion

    /// ...
}

Ответ 8

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

Public MustInherit Class Singleton(Of T As {Class, New})
    Public Sub New()
    End Sub

    Private Class SingletonCreator
        Shared Sub New()
        End Sub
        Friend Shared ReadOnly Instance As New T
    End Class

    Public Shared ReadOnly Property Instance() As T
        Get
            Return SingletonCreator.Instance
        End Get
    End Property
End Class

Ответ 9

В соответствии с запросом, перекрестная отправка из моего первоначального ответа на другой вопрос.

Моя версия использует 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;
}

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

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

Ответ 10

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

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

  private Singleton()
  {
  }

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

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

Ответ 11

Попробуйте этот общий класс Singleton, реализующий шаблон проектирования Singleton в потоковом безопасном и ленивом виде (спасибо 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);
            }
        }
    }
}

Ответ 12

Идиома 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()
{
}

Литература:

  • С# Cookbook (O'Reilly), Джей Хилярд и Стивен Тейхет.
  • С# 3.0 Design Patterns (O'Reilly), Джудит Бишоп
  • CSharp-Online.Net - Синглтонский шаблон дизайна: Thread-safe Singleton

Ответ 13

Мне очень понравился ваш оригинальный ответ - единственное, чего не хватает (по ссылке, помеченной blowdart), - сделать переменную _instance изменчивой, чтобы убедиться, что она действительно установлена ​​в блокировке. Я действительно использую решение blowdarts, когда мне нужно использовать синглтон, но мне не нужна поздняя поддержка и т.д.

Ответ 14

Мой вклад для обеспечения по запросу создания данных экземпляра:


/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name="T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
    private static object __SYNC = new object();
    private static volatile bool _IsInstanceCreated = false;
    private static T _Instance = default(T);

/// <summary>Instance data</summary> public static T Instance { get { if (!_IsInstanceCreated) lock (__SYNC) if (!_IsInstanceCreated) _Instance = Activator.CreateInstance<T>(); return _Instance; } } }

Ответ 15

pff... снова...:)
Мой вклад в обеспечение по требованию создания данных экземпляра:


/// <summary>Abstract base class for thread-safe singleton objects</summary>
/// <typeparam name="T">Instance type</typeparam>
public abstract class SingletonOnDemand<T> {
  private static object __SYNC = new object();
  private static volatile bool _IsInstanceCreated = false;
  private static T _Instance = default(T);

  /// <summary>Instance data</summary>
  public static T Instance {
    get {
      if (!_IsInstanceCreated)
        lock (__SYNC)
          if (!_IsInstanceCreated) {
            _Instance = Activator.CreateInstance<T>();
            _IsInstanceCreated = true;
          }
          return _Instance;
    }
  }
}

Ответ 16

public static class LazyGlobal<T> where T : new()
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

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

// user code:
{
    LazyGlobal<Foo>.Instance.Bar();
}

Или:

public delegate T Func<T>();

public static class CustomGlobalActivator<T>
{
    public static Func<T> CreateInstance { get; set; }
}

public static class LazyGlobal<T>
{
    public static T Instance
    {
        get { return TType.Instance; }
    }

    private static class TType
    {
        public static readonly T Instance = CustomGlobalActivator<T>.CreateInstance();
    }
}

{
    // setup code:
    // CustomGlobalActivator<Foo>.CreateInstance = () => new Foo(instanceOf_SL_or_IoC.DoSomeMagicReturning<FooDependencies>());
    CustomGlobalActivator<Foo>.CreateInstance = () => instanceOf_SL_or_IoC.PleaseResolve<Foo>();
    // ...
    // user code:
    LazyGlobal<Foo>.Instance.Bar();
}

Ответ 17

Saw один назад, который использует отражение для доступа к закрытому (или общедоступному) конструктору по умолчанию:

public static class Singleton<T>
{
    private static object lockVar = new object();
    private static bool made;
    private static T _singleton = default(T);

    /// <summary>
    /// Get The Singleton
    /// </summary>
    public static T Get
    {
        get
        {
            if (!made)
            {
                lock (lockVar)
                {
                    if (!made)
                    {
                        ConstructorInfo cInfo = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null);
                        if (cInfo != null)
                            _singleton = (T)cInfo.Invoke(new object[0]);
                        else
                            throw new ArgumentException("Type Does Not Have A Default Constructor.");
                        made = true;
                    }
                }
            }

            return _singleton;
        }
    }
}

Ответ 18

Я передаю это группе. Кажется, что это поточно-безопасный, общий и следует шаблону. Вы можете унаследовать его. Это вытолкнуто из того, что говорили другие.

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

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

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

Предполагаемая реализация:

public class Foo: Singleton<Foo>
{
     private Foo() { }
}

Тогда:

Foo.Instance.SomeMethod();

Ответ 19

Как в wikipedia:

шаблон singleton - это шаблон проектирования, который ограничивает создание экземпляра класса для одного объекта

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

public class MyClass
{
        private MyClass()
        {

        }

        static MyClass()
        {
            Instance = new MyClass();
        }

        public static MyClass Instance { get; private set; }
}

ИЛИ

public class MyClass
    {
            private MyClass()
            {

            }

            static MyClass()
            {
                Instance = new MyClass();
            }

            private static MyClass instance;



            public static MyClass Instance
            {
                get
                {
                    return instance;
                }
                private set
                {
                    instance = value;
                }
            }
    }

Ответ 20

Это работает для меня:

public static class Singleton<T> 
{
    private static readonly object Sync = new object();

    public static T GetSingleton(ref T singletonMember, Func<T> initializer)
    {
        if (singletonMember == null)
        {
            lock (Sync)
            {
                if (singletonMember == null)
                    singletonMember = initializer();
            }
        }
        return singletonMember;
    }
}

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

private static MyType _current;
public static MyType Current = Singleton<MyType>.GetSingleton(ref _current, () => new MyType());

Используйте синглтон:

MyType.Current. ...

Ответ 21

Независимо от того, какой пример вы выберете, всегда проверяйте параллелизм с помощью Parallel.For! (цикл, в котором итерации могут выполняться параллельно)

положить в Singleton C'tor:

 private Singleton ()
        {
            Console.WriteLine("usage of the Singleton for the first time");
        }

положить в основной:

Parallel.For(0, 10,
              index => {
                  Thread tt = new Thread(new ThreadStart(Singleton.Instance.SomePrintMethod));
                  tt.Start();
              });

Ответ 22

Вам не нужно все это, у С# уже есть хороший одноэлементный шаблон.

static class Foo

Если вам нужно что-то более интересное, скорее всего, ваш новый синглтон будет совсем другим, чтобы ваш общий шаблон был бесполезным.

EDIT: "Что-то более интересное" я включаю в себя наследование. Если вы можете наследовать один сингл, это уже не сингл.