通用单例<t> </t>

时间:2010-02-23 14:59:53

标签: c# oop design-patterns singleton

我有一个问题,这是制作Generic Singleton的正确方法吗?

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

修改

检查一些PDF我发现一个通用的Singleton以另一种方式制作,这是另一个正确吗?

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

9 个答案:

答案 0 :(得分:19)

通用单件工厂的问题在于,由于它是通用的,因此您无法控制实例化的“单例”类型,因此您永远无法保证您创建的实例将是应用程序中的唯一实例。

如果用户可以将类型作为泛型类型参数提供,那么他们也可以创建该类型的实例。换句话说,你不能创建一个通用的单件工厂 - 它会破坏模式本身。

答案 1 :(得分:5)

这是我使用.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 :(得分:2)

这是我使用非公共构造函数的实现。现在唯一的问题是没有办法对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) { }
}

答案 3 :(得分:1)

您可以使用一些作弊(反射)来制作单例基类。 您可以(运行时)强制执行没有公共构造函数的类。

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

答案 4 :(得分:0)

对于将被重用的通用代码段,您应该在创建单例实例时考虑线程安全性。

实际上,(instance == null) 可以在不同的线程上评估为true。

答案 5 :(得分:0)

  

要创建通用的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. 构造函数方法受保护不公开

答案 6 :(得分:0)

这是我如何使用当前模式(也是线程安全初始化)

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. ...

答案 7 :(得分:0)

没有反思就有可能。

我们只需要一个单例模式的泛型类,它带有两个参数 - 具体单例类的实现和具体单例的接口。 通用单例类实现单例模式和所需的所有东西 - 例如日志记录,锁定或其他什么。

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.");
            }
        }
    }
}

答案 8 :(得分:0)

这是Cpp中的示例,但您可以将其转换为任何其他语言:

var obj = /*ur obj*/
var itemID = 987654;
var n = 1;

removeItem(itemID, n) {
  let str = obj[itemID][n];
  this.obj[itemID] = obj[itemID].filter(item => item != str);
}