Универсальный синглтон <T> - PullRequest
7 голосов
/ 23 февраля 2010

У меня вопрос, это правильный подход к созданию универсального синглтона?

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

        private Singleton() { }

        public static T Instancia
        {
            get 
            {
                if (instance == null)
                    instance = new T();
                return instance;
            }
        }
    }

EDIT:

Проверяя некоторые PDF-файлы, я обнаружил, что универсальный синглтон сделан по-другому, это правильно?

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

Ответы [ 8 ]

19 голосов
/ 23 февраля 2010

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

Если пользователь может предоставить тип в качестве аргумента универсального типа, он также может создавать экземпляры этого типа.Другими словами, вы не можете создать универсальную фабрику синглтона - это подрывает сам шаблон.

5 голосов
/ 31 октября 2011

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

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

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

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

Шаблон использования:

var journalSingleton = Singleton<JournalClass>.Instance;
2 голосов
/ 18 января 2012

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

using System;
using System.Reflection;
using System.Threading;

/// <summary>
/// A generic abstract implementation of the Singleton design pattern (http://en.wikipedia.org/wiki/Singleton_pattern).
/// 
/// Derived type must contain a non-public default constructor to satisfy the rules of the Singleton Pattern.
/// If no matching constructor is found, an exception will be thrown at run-time. I am working on a StyleCop
/// constraint that will throw a compile-time error in the future.
/// 
/// Example Usage (C#):
/// 
///     class MySingleton : Singleton&lt;MySingleton&gt;
///     {
///         private const string HelloWorldMessage = "Hello World - from MySingleton";
///     
///         public string HelloWorld { get; private set; }
///
///         // Note: *** Private Constructor ***
///         private MySingleton()
///         {
///             // Set default message here.
///             HelloWorld = HelloWorldMessage;
///         }
///     }
/// 
///     class Program
///     {
///         static void Main()
///         {
///             var mySingleton = MySingleton.Instance;
///             Console.WriteLine(mySingleton.HelloWorld);
///             Console.ReadKey();
///         }
///     }
/// </summary>
/// <typeparam name="T">Type of derived Singleton object (i.e. class MySingletone: Singleton&lt;MySingleton&gt;).</typeparam>
public abstract class Singleton<T> where T : class
{
    /// <summary>
    /// "_instance" is the meat of the Singleton<T> base-class, as it both holds the instance
    /// pointer and the reflection based factory class used by Lazy&lt;T&gt; for instantiation.
    /// 
    /// Lazy&lt;T&gt;.ctor(Func&lt;T&gt; valueFactory,LazyThreadSafetyMode mode), valueFactory:
    /// 
    ///     Due to the fact Lazy&lt;T&gt; cannot access a singleton's (non-public) default constructor and
    ///     there is no "non-public default constructor required" constraint available for C# 
    ///     generic types, Lazy&lt;T&gt;'s valueFactory Lambda uses reflection to create the instance.
    ///
    /// Lazy&lt;T&gt;.ctor(Func&lt;T&gt; valueFactory,LazyThreadSafetyMode mode), mode:
    /// 
    ///     Explanation of selected mode (ExecutionAndPublication) is from MSDN.
    ///     
    ///     Locks are used to ensure that only a single thread can initialize a Lazy&lt;T&gt; instance 
    ///     in a thread-safe manner. If the initialization method (or the default constructor, if 
    ///     there is no initialization method) uses locks internally, deadlocks can occur. If you 
    ///     use a Lazy&lt;T&gt; constructor that specifies an initialization method (valueFactory parameter),
    ///     and if that initialization method throws an exception (or fails to handle an exception) the 
    ///     first time you call the Lazy&lt;T&gt;.Value property, then the exception is cached and thrown
    ///     again on subsequent calls to the Lazy&lt;T&gt;.Value property. If you use a Lazy&lt;T&gt; 
    ///     constructor that does not specify an initialization method, exceptions that are thrown by
    ///     the default constructor for T are not cached. In that case, a subsequent call to the 
    ///     Lazy&lt;T&gt;.Value property might successfully initialize the Lazy&lt;T&gt; instance. If the
    ///     initialization method recursively accesses the Value property of the Lazy&lt;T&gt; instance,
    ///     an InvalidOperationException is thrown.
    /// 
    /// </summary>
    private static readonly Lazy<T> _instance = new Lazy<T>(() =>
                                                                {
                                                                    // Get non-public constructors for T.
                                                                    var ctors = typeof (T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);

                                                                    // If we can't find the right type of construcor, throw an exception.
                                                                    if (!Array.Exists(ctors, (ci) => ci.GetParameters().Length == 0))
                                                                    {
                                                                        throw new ConstructorNotFoundException("Non-public ctor() note found.");
                                                                    }

                                                                    // Get reference to default non-public constructor.
                                                                    var ctor = Array.Find(ctors, (ci) => ci.GetParameters().Length == 0);

                                                                    // Invoke constructor and return resulting object.
                                                                    return ctor.Invoke(new object[] {}) as T;
                                                                }, LazyThreadSafetyMode.ExecutionAndPublication);

    /// <summary>
    /// Singleton instance access property.
    /// </summary>
    public static T Instance
    {
        get { return _instance.Value; }
    }
}

/// <summary>
/// Exception thrown by Singleton&lt;T&gt; when derived type does not contain a non-public default constructor.
/// </summary>
public class ConstructorNotFoundException : Exception
{
    private const string ConstructorNotFoundMessage = "Singleton<T> derived types require a non-public default constructor.";
    public ConstructorNotFoundException() : base(ConstructorNotFoundMessage) { }
    public ConstructorNotFoundException(string auxMessage) : base(String.Format("{0} - {1}", ConstructorNotFoundMessage, auxMessage)) { }
    public ConstructorNotFoundException(string auxMessage, Exception inner) : base(String.Format("{0} - {1}", ConstructorNotFoundMessage, auxMessage), inner) { }
}
1 голос
/ 17 сентября 2017

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

public abstract class Singleton<T> where T : Singleton<T> {
    private const string ErrorMessage = " must have a parameterless constructor and all constructors have to be NonPublic.";
    private static T instance = null;
    public static T Instance => instance ?? (instance = Create());

    protected Singleton() {
        //check for public constructors
        var pconstr = typeof(T).GetConstructors(BindingFlags.Public | BindingFlags.Instance);
        //tell programmer to fix his stuff
        if (pconstr.Any())
            throw new Exception(typeof(T) + ErrorMessage);
    }

    private static T Create() {
        try {
            //get nonpublic constructors
            var constructors = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
            //make sure there is but 1 and use that
            return (T)constructors.Single().Invoke(null);
        }
        catch {
            //tell programmer to fix his stuff
            throw new Exception(typeof(T)+ErrorMessage);
        }
    }
}
0 голосов
/ 08 января 2018

Это возможно без отражения.

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

using System;
using System.Diagnostics;

namespace Singleton
{
    class Program
    {
        static void Main(string[] args)
        {
            Something.Instance.SayHello();
        }
    }

    /// <summary>
    /// Generic singleton pattern implementation
    /// </summary>
    public class SingletonImplementation<Implementation, ImplementationInterface>
           where Implementation : class, ImplementationInterface, new()
    {
        private SingletonImplementation() { }

        private static Implementation instance = null;
        public static ImplementationInterface Instance
        {
            get
            {
                // here you can add your singleton stuff, which you don't like to write all the time

                if ( instance == null )
                {
                    instance = new Implementation();
                }

                return instance;
            }
        }
    }

    /// <summary>
    /// Interface for the concrete singleton
    /// </summary>
    public interface ISomething
    {
        void SayHello();
    }

    /// <summary>
    /// Singleton "held" or "wrapper" which provides the instance of the concrete singleton
    /// </summary>
    public static class Something
    {
        // No need to define the ctor private, coz you can't do anything wrong or useful with an instance of Something
        // private Implementation();

        /// <summary>
        /// Like common: the static instance to access the concrete singleton
        /// </summary>
        public static ISomething Instance => SingletonImplementation<ImplementationOfSomething, ISomething>.Instance;

        /// <summary>
        /// Concrete singleton implementation
        /// </summary>
        private class ImplementationOfSomething : ISomething
        {
            // No need to define the ctor private, coz the class is private.
            // private Implementation();

            public void SayHello()
            {
                Debug.WriteLine("Hello world.");
            }
        }
    }
}
0 голосов
/ 14 сентября 2017

Вот как я это сделал, используя шаблон Current (также инициализирующий потокобезопасность)

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. ...
0 голосов
/ 23 февраля 2015

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

public abstract class BaseLazySingleton<T> where T : class
    {
        private static readonly Lazy<T> LazyInstance =
            new Lazy<T>(CreateInstanceOfT, LazyThreadSafetyMode.ExecutionAndPublication);

        #region Properties
        public static T Instance
        {
            get { return LazyInstance.Value; }
        }
        #endregion

        #region Methods
        private static T CreateInstanceOfT()
        {
            return Activator.CreateInstance(typeof(T), true) as T;
        }

        protected BaseLazySingleton()
        {
        }

        #endregion
    }

Обратите внимание, что

  1. этот генератор абстрактный, поэтому никто не может создать новый экземпляр этого класса.
  2. метод конструктора защищен Не публично
0 голосов
/ 23 февраля 2010

Для общего фрагмента кода, который будет использоваться повторно, вы должны учитывать безопасность потоков в том месте, где вы создаете экземпляр singleton.

Как таковое, (instance == null) может оценить как true в отдельных потоках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...