2008-12-19 10 views
19

¿Qué piensan sobre esto para un singleton genérico?Un singleton genérico

using System; 
using System.Reflection; 

// Use like this 
/* 
public class Highlander : Singleton<Highlander> 
{ 
    private Highlander() 
    { 
     Console.WriteLine("There can be only one..."); 
    } 
} 
*/ 

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

    public static T GetInstance() 
    { 
     if (instance == null) 
     { 
      CreateInstance(); 
     } 

     return instance; 
    } 

    private static void CreateInstance() 
    { 
     lock (initLock) 
     { 
      if (instance == null) 
      { 
       Type t = typeof(T); 

       // Ensure there are no public constructors... 
       ConstructorInfo[] ctors = t.GetConstructors(); 
       if (ctors.Length > 0) 
       { 
        throw new InvalidOperationException(String.Format("{0} has at least one accesible ctor making it impossible to enforce singleton behaviour", t.Name)); 
       } 

       // Create an instance via the private constructor 
       instance = (T)Activator.CreateInstance(t, true); 
      } 
     } 
    } 
} 
+8

+1 para el nombre Highlander ;-) –

+0

Esta técnica de bloqueo se rompe a menos que se utilice una palabra clave volátil para el objeto de bloqueo. Ver [esto] (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) –

+0

Pregunta buena para codereview.stackexchange.com – MPelletier

Respuesta

28

Creación de una clase Singleton está a sólo unas pocas líneas de código, y con la dificultad de hacer un producto único genérico Siempre escribo estas líneas de código.

public class Singleton 
{ 
    private Singleton() {} 
    static Singleton() {} 
    private static Singleton _instance = new Singleton(); 
    public static Singleton Instance { get { return _instance; }} 
} 

La línea

private static Singleton _instance = new Singleton(); 

elimina la necesidad de bloqueo, como un constructor estático es seguro para subprocesos.

+5

Exactamente. Especialmente dado que la versión genérica en realidad no le da un singleton de todos modos ... –

+1

Probablemente también deba agregar un constructor estático. Ver http://www.yoda.arachsys.com/csharp/singleton.html –

+0

De hecho. Y para la carga diferida, la página de Jon lo cubre también con una clase anidada. –

5

Bueno, en realidad no es Singleton - ya que no puede controlar T, no puede haber tantos casos T como desee.

(rosca a la carrera eliminado; observó el uso de doble marcado)

5

He borrado mi respuesta anterior ya que no había notado el código que busca constructores no públicos. Sin embargo, esta es una comprobación que solo se realiza en el momento de la ejecución: no hay una comprobación en tiempo de compilación, lo cual es un golpe en su contra. También depende de tener acceso suficiente para llamar al constructor no público, lo que agrega algunas limitaciones.

Además, no prohíbe constructores internos - por lo que puede terminar con no-singletons.

Yo personalmente crearía la instancia en un constructor estático para seguridad de hilos simples, también.

Básicamente no soy muy entusiasta, es bastante fácil crear clases singleton, y no deberías hacerlo de todas maneras. Hijos únicos son un dolor para la prueba, el desacoplamiento etc.

+1

de acuerdo; en la mayoría de los casos, una clase estática es más apropiada. Útil para implementar interfaces, etc., pero el patrón estándar funciona bien sin necesidad de los genéricos. –

+2

No olvides el enlace ... ¡Jon! :) http://www.yoda.arachsys.com/csharp/singleton.html – kenny

+0

@kenny: Ese enlace es justo lo que necesitaba. ¡¡Gracias!! – fourpastmidnight

5

Este es mi punto utilizando .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; } } 
    } 

y se está usando es el siguiente:

public class Adaptor 
    { 
    public static Adaptor Instance { get { return Singleton<Adaptor>.Instance;}} 
    } 
+3

El problema con este enfoque es que requiere un constructor público para la clase de implementación singleton, que permite la posibilidad de uso fuera de los límites de una instancia singleton (es decir, la prevención del tiempo de compilación no está presente aquí). – sweetfa

+0

Este es el costo de sus genéricos. – Alexandr

+1

Sí, solo tiene que ser obvio para el novato, aunque – sweetfa

1

La fusión de respuesta AndreasN y Jon Skeet de "Fourth version - not quite as lazy, but thread-safe without using locks "de una implementación Singleton C#, ¿por qué no utilizar un fragmento de código para hacer todo el trabajo duro:

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> 
    <CodeSnippet Format="1.0.0"> 
     <Header> 
      <Title>Singleton Class</Title> 
      <Author>TWSoft</Author> 
      <Description>Generates a singleton class</Description> 
      <SnippetTypes> 
       <SnippetType>Expansion</SnippetType> 
      </SnippetTypes> 
      <Keywords> 
       <Keyword>Singleton</Keyword> 
      </Keywords> 
      <Shortcut>singleton</Shortcut> 
     </Header> 
     <Snippet> 
      <Declarations> 
       <Literal> 
        <ID>ClassName</ID> 
        <ToolTip>Replace with class name</ToolTip> 
        <Default>MySingletonClass</Default> 
       </Literal> 
      </Declarations> 

      <Code Language="CSharp"> 
       <![CDATA[ 
       public class $ClassName$ 
       { 
        #region Singleton 
        static readonly $ClassName$ mInstance = new $ClassName$(); 

        // Explicit static constructor to tell C# compiler 
        // not to mark type as beforefieldinit 
        static $ClassName$() 
        { 
        } 

        private $ClassName$() 
        { 
        } 

        public static $ClassName$ Instance 
        { 
         get { return mInstance; } 
        } 
       #endregion 
       } 
       ]]> 
      </Code> 
     </Snippet> 
    </CodeSnippet> 
</CodeSnippets> 

Luego puede guardar esto en un archivo .snippt y agregarlo a VS IDE (Herramientas-> Administrador de fragmentos de código)

-1

No creo que el uso de genéricos sea útil para singletons. Porque siempre puedes crear varias instancias y por lo tanto es no por definición un singleton. Si necesita un producto único perezoso y necesita que sea un verdadero Singleton una solución simple (basado en el ejemplo de Alexandr)

public sealed class Adaptor 
{ 
    private static readonly Lazy<Adaptor> instance = new Lazy<Adaptor>(() => new Adaptor()); 

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

    private Adaptor() { } 
} 

No se puede refactorizar esto correctamente en un producto único genérico separado.

Consulte también: http://csharpindepth.com/Articles/General/Singleton.aspx

0

Hay un problema con una fábrica Singleton genérico, ya que es genérico, no se controla el tipo singleton que se crea una instancia, por lo que puede Nunca garantizar que la instancia se crea será la única instancia en la aplicación.

Por lo tanto, no puede crear una fábrica de singleton genérica: socava el patrón en sí mismo.