C# Language
Implementación Singleton
Buscar..
Singleton estáticamente inicializado
public class Singleton
{
private readonly static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton Instance => instance;
}
Esta implementación es segura para subprocesos porque en este caso instance
objeto de instance
se inicializa en el constructor estático. El CLR ya garantiza que todos los constructores estáticos se ejecuten seguros para subprocesos.
La instance
mutante no es una operación segura para subprocesos, por lo tanto, el atributo de readonly
garantiza la inmutabilidad después de la inicialización.
Singleton perezoso, seguro para subprocesos (con bloqueo de doble control)
Esta versión segura para subprocesos de un singleton era necesaria en las versiones anteriores de .NET donde no se garantizaba que static
inicialización static
fuera segura para subprocesos. En versiones más modernas del marco, generalmente se prefiere un singleton estáticamente inicializado porque es muy fácil cometer errores de implementación en el siguiente patrón.
public sealed class ThreadSafeSingleton
{
private static volatile ThreadSafeSingleton instance;
private static object lockObject = new Object();
private ThreadSafeSingleton()
{
}
public static ThreadSafeSingleton Instance
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}
}
}
Observe que la verificación if (instance == null)
se realiza dos veces: una vez antes de que se adquiera el bloqueo y una vez después. Esta implementación aún sería segura para subprocesos incluso sin la primera comprobación nula. Sin embargo, eso significaría que se adquirirá un bloqueo cada vez que se solicite la instancia, y eso causaría un deterioro en el rendimiento. La primera comprobación nula se agrega para que el bloqueo no se obtenga a menos que sea necesario. La segunda comprobación nula se asegura de que solo la primera hebra para adquirir el bloqueo cree la instancia. Los otros subprocesos encontrarán la instancia que se va a rellenar y seguir adelante.
Singleton perezoso, seguro para hilos (usando perezoso )
El tipo .Net 4.0 Lazy garantiza la inicialización de objetos seguros para subprocesos, por lo que este tipo podría usarse para hacer Singletons.
public class LazySingleton
{
private static readonly Lazy<LazySingleton> _instance =
new Lazy<LazySingleton>(() => new LazySingleton());
public static LazySingleton Instance
{
get { return _instance.Value; }
}
private LazySingleton() { }
}
El uso de Lazy<T>
se asegurará de que el objeto solo se ejecute cuando se use en algún lugar del código de llamada.
Un uso simple será como:
using System;
public class Program
{
public static void Main()
{
var instance = LazySingleton.Instance;
}
}
Singleton seguro, seguro para subprocesos (para .NET 3.5 o anterior, implementación alternativa)
Debido a que en .NET 3.5 y versiones anteriores no tienes la clase Lazy<T>
, usas el siguiente patrón:
public class Singleton
{
private Singleton() // prevents public instantiation
{
}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
Esto está inspirado en la publicación del blog de Jon Skeet .
Debido a que el Nested
clase anidada es privada y la creación de instancias de la instancia singleton no se activará mediante el acceso a otros miembros de la Sigleton
clase (como una propiedad pública de sólo lectura, por ejemplo).
Eliminación de la instancia de Singleton cuando ya no sea necesaria.
La mayoría de los ejemplos muestran la LazySingleton
instancias y la retención de un objeto LazySingleton
hasta que la aplicación propietaria haya finalizado, incluso si la aplicación ya no lo necesita. Una solución para esto es implementar IDisposable
y establecer la instancia del objeto en nulo de la siguiente manera:
public class LazySingleton : IDisposable
{
private static volatile Lazy<LazySingleton> _instance;
private static volatile int _instanceCount = 0;
private bool _alreadyDisposed = false;
public static LazySingleton Instance
{
get
{
if (_instance == null)
_instance = new Lazy<LazySingleton>(() => new LazySingleton());
_instanceCount++;
return _instance.Value;
}
}
private LazySingleton() { }
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
if (--_instanceCount == 0) // No more references to this object.
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_alreadyDisposed) return;
if (disposing)
{
_instance = null; // Allow GC to dispose of this instance.
// Free any other managed objects here.
}
// Free any unmanaged objects here.
_alreadyDisposed = true;
}
El código anterior elimina la instancia antes de la finalización de la aplicación, pero solo si los consumidores llaman a Dispose()
en el objeto después de cada uso. Dado que no hay garantía de que esto suceda o una forma de forzarlo, tampoco hay garantía de que la instancia se pueda disponer. Pero si esta clase se usa internamente, es más fácil asegurar que se llame al método Dispose()
después de cada uso. Un ejemplo sigue:
public class Program
{
public static void Main()
{
using (var instance = LazySingleton.Instance)
{
// Do work with instance
}
}
}
Tenga en cuenta que este ejemplo no es seguro para subprocesos .