¿Qué es un buen patrón de plantilla genérica de Singleton en C #?

Tengo el siguiente patrón singleton C #, ¿hay alguna forma de mejorarlo?

public class Singleton 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() { } } 

Ejemplo de uso preferido:

 class Foo : Singleton { } 

Relacionado :

¿Una implementación singleton obvia para .NET?

De acuerdo con Jon Skeet en Implementing Singleton Pattern en C #, el código que publicaste realmente se considera como código incorrecto, porque parece estar roto cuando se compara con el estándar ECMA CLI.

También ten cuidado: cada vez que instancias tu objeto con un nuevo tipo de T, se convierte en otra instancia; no se refleja en su singleton original.

Este código no se comstackrá, necesita restricción de “clase” en T.

Además, este código requiere un constructor público en la clase de destino, que no es bueno para singleton, porque no puede controlar en tiempo de comstackción que obtiene una instancia (única) solo a través de la propiedad Instancia (o campo). Si no tiene ningún otro miembro estático excepto Instancia, puede hacerlo con solo esto:

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

Es seguro para subprocesos (garantizado por CLR) y vago (la instancia se crea con el primer acceso al tipo). Para obtener más información sobre BeforeFieldInit y por qué necesitamos constructor estático aquí, consulte http://www.yoda.arachsys.com/csharp/beforefieldinit.html .

Si desea tener otros miembros estáticos públicos en el tipo, pero cree el objeto solo en el acceso a Instancia, puede crear un tipo nested, como en http://www.yoda.arachsys.com/csharp/singleton.html

Cortesía de Judith Bishop, http://patterns.cs.up.ac.za/

Esta implementación de patrón único asegura la inicialización lenta.

 // Singleton PatternJudith Bishop Nov 2007 // Generic version public class Singleton 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; } } } 

Este es mi punto usando .NET 4

 public class Singleton where T : class, new() { Singleton (){} private static readonly Lazy instance = new Lazy(()=> new T()); public static T Instance { get { return instance.Value; } } } 

Más detalles sobre esta respuesta en un hilo diferente: ¿Cómo implementar un singleton en C #?

Sin embargo, el hilo no usa genérico .

No creo que realmente quieras “grabar tu clase base” para que puedas guardar 2 líneas de código. Realmente no necesitas una clase base para implementar singleton.

Siempre que necesite un singleton, simplemente haga esto:

 class MyConcreteClass { #region Singleton Implementation public static readonly Instance = new MyConcreteClass(); private MyConcreteClass(){} #endregion /// ... } 
 public sealed class Singleton { private static readonly Singleton instance = new Singleton(); private Singleton(){} public static Singleton Instance { get { return instance; } } } 

No hay ambigüedad en .NET en torno al orden de inicialización ; pero esto plantea problemas de enhebrado.

Estaba buscando un mejor patrón Singleton y me gustó este. Así que lo portado a VB.NET, puede ser útil para otros:

 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 

Según lo solicitado, cruce la publicación de mi respuesta original a otra pregunta.

Mi versión usa Reflection, funciona con constructores no públicos en la clase derivada, es threadsafe (obviamente) con instanciación lenta (de acuerdo con el artículo que encontré vinculado a continuación):

 public class SingletonBase 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; } 

Recogí esto hace unos años, no estoy seguro de cuánto es mío, pero buscar en el código de Google podría encontrar la fuente original de la técnica si no fuera yo.

Esta es la fuente más antigua del código que puedo encontrar y no fui yo quien la publicó.

: / el patrón genérico “singleton” de judith obispo parece un tanto defectuoso, siempre es posible crear varias instancias de tipo T ya que el constructor debe ser público para usarlo en este “patrón”. En mi opinión, no tiene absolutamente nada que ver con singleton, es solo una especie de fábrica, que siempre devuelve el mismo objeto, pero no lo convierte en singleton … siempre que pueda haber más de 1 instancia de una clase. no puede ser un singleton ¿Hay alguna razón por la cual este patrón tenga la mejor calificación?

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

los inicializadores estáticos se consideran seguros contra subprocesos … no lo sé, pero no debería usar expresiones idiomáticas de singleton en absoluto, si ajusta mi código sobre sus no más de 3 líneas … y heredar de un singleton no hace cualquier sentido, ya sea

Pruebe esta clase genérica de Singleton implementando el patrón de diseño de Singleton de una manera segura y perezosa (thx to wcell).

 public abstract class Singleton where T : class { ///  /// Returns the singleton instance. ///  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); } } } } 

La expresión de locking doble [Lea99] proporcionada por Microsoft aquí es sorprendentemente similar a su código proporcionado, desafortunadamente, esto falla el estándar ECMA CLI para una vista puritana de código seguro para subprocesos y puede no funcionar correctamente en todas las situaciones.

En un progtwig de subprocesos múltiples, diferentes subprocesos podrían intentar crear una instancia de una clase simultáneamente. Por este motivo, una implementación de Singleton que se base en una instrucción if para comprobar si la instancia es nula no será segura para subprocesos . ¡No escriba código así!

Un medio simple pero eficaz de crear un singleton seguro para subprocesos es utilizar una clase anidada para crear una instancia. El siguiente es un ejemplo de singleton de creación de instancias perezosa:

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

Uso:

 Singleton s1 = Singleton.Instance; Singleton s2 = Singleton.Instance; if (s1.Equals(s2)) { Console.WriteLine("Thread-Safe Singleton objects are the same"); } 

Solución Genérica:

 public class Singleton 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(); } } 

Uso:

 class TestClass { } Singleton s1 = Singleton.Instance; Singleton s2 = Singleton.Instance; if (s1.Equals(s2)) { Console.WriteLine("Thread-Safe Generic Singleton objects are the same"); } 

Por último, aquí hay una sugerencia algo repetida y útil: para ayudar a evitar interlockings que pueden ser causados ​​por el uso de la palabra clave de locking, considere agregar el siguiente atributo para ayudar a proteger el código solo en métodos públicos estáticos:

 using System.Runtime.CompilerServices; [MethodImpl (MethodImplOptions.Synchronized)] public static void MySynchronizedMethod() { } 

Referencias

  1. C # Cookbook (O’Reilly), Jay Hilyard y Stephen Teilhet
  2. C # 3.0 Patrones de diseño (O’Reilly), Judith Bishop
  3. CSharp-Online.Net – Patrón de diseño Singleton: singleton seguro para subprocesos

Me gustó bastante su respuesta original: lo único que falta (de acuerdo con el enlace publicado por blowdart) es hacer que la variable _instance sea volátil, para asegurarse de que realmente se haya configurado en el locking. De hecho, uso la solución de blowdarts cuando tengo que usar un singleton, pero no necesito crear una instancia tardía, etc.

Mi contribución para garantizar la creación a demanda de datos de instancia:

 /// Abstract base class for thread-safe singleton objects /// Instance type public abstract class SingletonOnDemand { private static object __SYNC = new object(); private static volatile bool _IsInstanceCreated = false; private static T _Instance = default(T); 
/// Instance data public static T Instance { get { if (!_IsInstanceCreated) lock (__SYNC) if (!_IsInstanceCreated) _Instance = Activator.CreateInstance
(); return _Instance; } } }

pff … otra vez … 🙂
Mi contribución para garantizar la creación a demanda de datos de instancia:

 /// Abstract base class for thread-safe singleton objects /// Instance type public abstract class SingletonOnDemand { private static object __SYNC = new object(); private static volatile bool _IsInstanceCreated = false; private static T _Instance = default(T); /// Instance data public static T Instance { get { if (!_IsInstanceCreated) lock (__SYNC) if (!_IsInstanceCreated) { _Instance = Activator.CreateInstance(); _IsInstanceCreated = true; } return _Instance; } } } 
 public static class LazyGlobal 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.Instance.Bar(); } 

O:

 public delegate T Func(); public static class CustomGlobalActivator { public static Func CreateInstance { get; set; } } public static class LazyGlobal { public static T Instance { get { return TType.Instance; } } private static class TType { public static readonly T Instance = CustomGlobalActivator.CreateInstance(); } } { // setup code: // CustomGlobalActivator.CreateInstance = () => new Foo(instanceOf_SL_or_IoC.DoSomeMagicReturning()); CustomGlobalActivator.CreateInstance = () => instanceOf_SL_or_IoC.PleaseResolve(); // ... // user code: LazyGlobal.Instance.Bar(); } 

Vi hace un tiempo que utiliza la reflexión para acceder a un constructor predeterminado privado (o público):

 public static class Singleton { private static object lockVar = new object(); private static bool made; private static T _singleton = default(T); ///  /// Get The Singleton ///  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; } } } 

Lo presento al grupo. Parece ser seguro para subprocesos, genérico y sigue el patrón. Puedes heredar de eso. Esto está improvisado por lo que otros han dicho.

 public class Singleton 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; } } } 

Implementación prevista:

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

Entonces:

 Foo.Instance.SomeMethod(); 

Como en wikipedia :

el patrón singleton es un patrón de diseño que restringe la creación de instancias de una clase a un objeto

Supongo que no existe una forma garantizada de hacerlo mediante generics, si ha restringido la creación de instancias del singleton en sí, cómo restringir la creación de instancias de la clase principal, creo que no es posible hacerlo, y la implementación de este patrón simple no es tan difícil, toma esto usando el constructor estático y el conjunto privado:

 public class MyClass { private MyClass() { } static MyClass() { Instance = new MyClass(); } public static MyClass Instance { get; private set; } } 

O:

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

Esto funciona para mí:

 public static class Singleton { private static readonly object Sync = new object(); public static T GetSingleton(ref T singletonMember, Func initializer) { if (singletonMember == null) { lock (Sync) { if (singletonMember == null) singletonMember = initializer(); } } return singletonMember; } } 

Uso:

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

Consumir el singleton:

 MyType.Current. ... 

Sin importar qué opción elija, siempre verifique la concurrencia usando Parallel.For! (loop en el que las iteraciones pueden ejecutarse en paralelo)

poner en Singleton C’tor:

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

poner en Principal:

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

No necesita todo eso, C # ya tiene un buen patrón de singleton incorporado.

 static class Foo 

Si necesita algo más interesante que eso, es probable que su nuevo singleton sea lo suficientemente diferente como para que su patrón genérico sea inútil.

EDITAR: por “algo más interesante”, incluyo la herencia. Si puede heredar desde un singleton, ya no es un singleton.