2010-02-23 75 views
4

我有一個問題,這是正確的方法來使一個通用的單身人士?通用單身人士<T>

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文件,我發現一個通用的辛格爾頓使這個另一種方式,這是其他正確的嗎?

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

謝謝!!!

+0

如果這個你的目的是實現一種服務定位器的,那就不要:http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx – 2010-02-23 15:13:55

+0

你不能新增了單身。這就是我想說的。以及你的通用單例實現不是的原因。單身。 – 2010-02-23 15:34:14

回答

17

通用單體工廠的問題在於,由於它是通用的,因此您不控制實例化的「單例」類型,因此您無法保證您創建的實例將成爲應用程序中的唯一實例。

如果用戶可以提供類型作爲泛型類型參數,那麼他們也可以創建該類型的實例。換句話說,你不能創建一個通用的單體工廠 - 它破壞了模式本身。

+0

可以將該類型保存在靜態數組中嗎? – Franz 2010-02-23 15:03:03

+0

@Franz - 我不確定你的意思 - 你可以用不同的方式提出你的問題嗎? – 2010-02-23 15:04:14

+0

那麼你可以使構造函數受到保護,並使模板類成爲朋友。那麼只有Singleton類可以創建實例。 糟糕,我不知道在C#中是否存在朋友,想到C++。 – monoceres 2010-02-23 15:04:59

0

對於將被重用的通用代碼片段,您應該在創建單例實例的位置考慮線程安全性。

就這樣,(instance == null)可能在單獨的線程上評估爲true。

+0

好的一點,雖然實際上,有很多構造不是線程安全的,包括我們每天使用的大多數集合。 – Nick 2010-02-23 15:28:01

+0

@尼克:倡導不良行爲,因爲存在不良行爲非常差。 – 2010-02-23 15:31:30

+1

@傑夫 - 我認爲它根本不提倡糟糕的行爲。大多數應用程序不需要是線程安全的,因此並不總是值得這些週期來確保它沒有這個要求。這是主觀的。說一個解決方案是不好的,因爲它沒有考慮到大多數人不會使用的用法並不一定是有效的。 – Nick 2010-02-23 15:43:54

2

這是使用.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; } } 
    } 
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) { } 
} 
0

用於創建通用辛格爾頓工廠,你可以使用這樣的類作爲您的工廠:

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

這我是怎麼做的,使用目前的模式(也線程安全初始化)

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

你可以使用一點作弊(反射)來創建單例基類。 你可以(運行時)強制一個類沒有公共構造函數。

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

這是可能的沒有反思。

我們只需要一個帶有兩個參數的單例模式的泛型類 - 具體單例類的實現和具體單例的接口。 通用的單例類實現了單例模式和所需的所有東西 - 例如日誌記錄,鎖定或其他。

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