¿Qué es un singleton en C #?

182

¿Qué es un Singleton y cuándo debo usarlo?

Sergio Tapia
fuente
2
Posible duplicado: stackoverflow.com/questions/246710/…
Mark Seemann
44
Además, el Singleton es uno de los patrones de diseño más utilizados y abusados ​​en la programación OO.
ChaosPandion
3
@Fabiano: Porque tiene una forma de crear acoplamientos que no tienen sentido (¿cómo puedo Xhablar con ellos Y? ¡Simplemente haga Yun singleton!), Lo que a su vez conduce a dificultades de prueba / depuración y un estilo de programación de procedimiento. Algunas veces los Singletons son necesarios; la mayoría de las veces, no.
Aaronaught
3
Esta es una de mis preguntas estándar para entrevistas telefónicas. La respuesta correcta es: nunca.
jonnii
3
@jonnii eso es bueno, ¡ayuda a advertir a los posibles desarrolladores cómo es su jefe!
Sr. Boy

Respuestas:

145

Un singleton es una clase que solo permite que se cree una instancia de sí mismo, y proporciona acceso simple y fácil a dicha instancia. La premisa singleton es un patrón en todo el desarrollo de software.

Hay una implementación de C # "Implementación del patrón Singleton en C #" que cubre la mayor parte de lo que necesita saber, incluidos algunos buenos consejos sobre la seguridad de los hilos .

Para ser honesto, es muy raro que necesite implementar un singleton; en mi opinión, debería ser una de esas cosas que debe tener en cuenta, incluso si no se usa con demasiada frecuencia.

Daniel May
fuente
2
bonito tutorial pero mierda santa, ¿qué le hicieron a la sangría del código?
Inspi
Aquí hay un enlace más directo a lo que considero la implementación ideal en 2020. Es decir, " usando el tipo Lazy <T> de .NET 4 ", así como el enlace al Microsoft Doc para el Lazy<T> Class.
Chiramisu
52

Solicitaste C #. Ejemplo trivial:


public class Singleton
{
    private Singleton()
    {
        // Prevent outside instantiation
    }

    private static readonly Singleton _singleton = new Singleton();

    public static Singleton GetSingleton()
    {
        return _singleton;
    }
}
Chris Simmons
fuente
14
no es seguro para subprocesos. Dos subprocesos pueden llamar al mismo tiempo y pueden crear dos objetos separados.
Alagesan Palani
55
@Alagesan Palani, de hecho tienes razón. No soy competente en los detalles de bajo nivel de inicialización a nivel de clase, pero creo que el cambio que hice aborda la preocupación de seguridad de subprocesos.
Chris Simmons
3
Claro, no estoy señalando que estás equivocado. Estoy dando una pista al lector sobre la seguridad del hilo, para que tengan cuidado si tienen que lidiar con eso.
Alagesan Palani
9
No, creo que tu comentario es importante. Dado que se supone que un singleton entrega una, y solo una, instancia, la condición de carrera aquí abre la posibilidad de que se entregue más de una. Vea la versión ahora, con inicialización de campo estático. Creo que esto soluciona el problema de seguridad de subprocesos, si leo los documentos y SO responde correctamente.
Chris Simmons
1
@AlagesanPalani, veo que has declarado que varias otras respuestas no son seguras para subprocesos. ¿Le gustaría proporcionar una solución segura para subprocesos?
Bonez024
39

Qué es: una clase para la que solo hay una instancia persistente durante la vida útil de una aplicación. Ver Patrón Singleton .

Cuándo debe usarlo: lo menos posible. Solo cuando esté absolutamente seguro de que lo necesita. Soy reacio a decir "nunca", pero generalmente hay una mejor alternativa, como la inyección de dependencia o simplemente una clase estática.

Aaronaught
fuente
16
No estoy seguro de que una clase estática sea una mejor alternativa que un singleton ... realmente depende de la situación y el idioma.
marcgg
55
Las clases estáticas no se comportan de la misma manera que un singleton, un singleton se puede pasar a los métodos como un parámetro, mientras que una clase estática no.
TabbyCool
44
De acuerdo con marcgg: no veo una clase estática como una buena alternativa a los singletons, porque todavía tiene el problema de proporcionar un sustituto, por ejemplo, durante la prueba de un componente que depende de esta clase. Pero también veo diferentes usos, una clase estática generalmente se usaría para funciones de utilidad independientes que son independientes del estado, donde un singleton es una instancia de clase real, y típicamente almacenaría un estado. Estoy completamente de acuerdo en usar DI en su lugar, y luego le digo a su contenedor DI que desea que use solo una instancia de esa clase.
Pete
9
Desestimé esta respuesta porque no me da información sobre cuándo usarla. "Solo cuando lo necesitas" en realidad no me da ninguna información para alguien nuevo en singletons.
Sergio Tapia
9
@Adkins: DI significa Inyección de dependencias, que es cuando cualquier dependencia de clase se pasa a través de (generalmente) un constructor o propiedad pública. DI solo no resuelve el problema de "distancia", pero generalmente se implementa junto con un contenedor de Inversión de control (IoC) que sabe cómo inicializar automáticamente cualquier dependencia. Entonces, si está creando un Singleton para resolver el problema "X no sabe cómo encontrar / hablar con Y", una combinación de DI e IoC puede resolver el mismo problema con un acoplamiento más flexible.
Aaronaught
27

Otra forma de implementar Singleton en C #, personalmente prefiero esta manera porque puede acceder a la instancia de la clase Singeton como una propiedad en lugar de un método.

public class Singleton
    {
        private static Singleton instance;

        private Singleton() { }

        public static Singleton Instance
        {
            get
            {
                if (instance == null)
                    instance = new Singleton();
                return instance;
            }
        }

        //instance methods
    }

pero bueno, que yo sepa, ambas formas se consideran "correctas", por lo que es solo una cuestión de sabor personal.

Marnix v. R.
fuente
11
no es seguro para subprocesos. Dos subprocesos pueden llamar al mismo tiempo y pueden crear dos objetos separados.
Alagesan Palani
11
using System;
using System.Collections.Generic;
class MainApp
{
    static void Main()
    {
        LoadBalancer oldbalancer = null;
        for (int i = 0; i < 15; i++)
        {
            LoadBalancer balancerNew = LoadBalancer.GetLoadBalancer();

            if (oldbalancer == balancerNew && oldbalancer != null)
            {
                Console.WriteLine("{0} SameInstance {1}", oldbalancer.Server, balancerNew.Server);
            }
            oldbalancer = balancerNew;
        }
        Console.ReadKey();
    }
}

class LoadBalancer
{
    private static LoadBalancer _instance;
    private List<string> _servers = new List<string>();
    private Random _random = new Random();

    private static object syncLock = new object();

    private LoadBalancer()
    {
        _servers.Add("ServerI");
        _servers.Add("ServerII");
        _servers.Add("ServerIII");
        _servers.Add("ServerIV");
        _servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
        if (_instance == null)
        {
            lock (syncLock)
            {
                if (_instance == null)
                {
                    _instance = new LoadBalancer();
                }
            }
        }

        return _instance;
    }

    public string Server
    {
        get
        {
            int r = _random.Next(_servers.Count);
            return _servers[r].ToString();
        }
    }
}

Tomé el código de dofactory.com , nada tan sofisticado, pero creo que esto es muy bueno que los ejemplos con Foo y Bar, además del libro de Judith Bishop en C # 3.0 Design Patterns tiene un ejemplo sobre la aplicación activa en Mac Dock.

Si observa el código, en realidad estamos construyendo nuevos objetos para el bucle, de modo que eso crea un nuevo objeto pero reutiliza la instancia como resultado de lo cual oldbalancer y newbalancer tienen la misma instancia, ¿cómo? es debido a la palabra clave estática utilizada en la función GetLoadBalancer () , a pesar de tener un valor de servidor diferente que es una lista aleatoria, la estática en GetLoadBalancer () pertenece al tipo en sí y no a un objeto específico.

Además, hay un doble control de bloqueo aquí

if (_instance == null)
            {
                lock (syncLock)
                {
                    if (_instance == null)

desde de MSDN

La palabra clave de bloqueo asegura que un hilo no entre en una sección crítica de código mientras otro hilo está en la sección crítica. Si otro hilo intenta ingresar un código bloqueado, esperará, bloqueará, hasta que se libere el objeto.

por lo que se emite un bloqueo de exclusión mutua en todo momento, incluso si no es necesario, lo que es innecesario, por lo que tenemos una verificación nula.

Esperemos que ayude a despejar más.

Y por favor comente si entiendo que está dirigiendo formas equivocadas.

Sandip Bantawa
fuente
6

Un Singleton (y esto no está vinculado a C #, es un patrón de diseño OO) es cuando desea permitir que se cree UNA sola instancia de una clase en toda su aplicación. Los usos típicamente incluirían recursos globales, aunque lo diré por experiencia personal, a menudo son la fuente de un gran dolor.

BFree
fuente
5

Si bien solo puede haber una instancia de un singleton, no es lo mismo que una clase estática. Una clase estática solo puede contener métodos estáticos y nunca puede ser instanciada, mientras que la instancia de un singleton puede usarse de la misma manera que cualquier otro objeto.

TabbyCool
fuente
2

Es un patrón de diseño y no es específico de c #. Obtenga más información al respecto en Internet y SO, como en este artículo de Wikipedia .

En ingeniería de software, el patrón singleton es un patrón de diseño que se utiliza para restringir la creación de instancias de una clase a un objeto. Esto es útil cuando se necesita exactamente un objeto para coordinar acciones en todo el sistema. El concepto a veces se generaliza a sistemas que funcionan de manera más eficiente cuando solo existe un objeto, o que restringen la creación de instancias a un cierto número de objetos (por ejemplo, cinco). Algunos lo consideran un antipatrón, ya que consideran que se usa en exceso, introduce limitaciones innecesarias en situaciones en las que no se requiere una única instancia de una clase e introduce el estado global en una aplicación.

Debe usarlo si desea una clase que solo pueda instanciarse una vez.

marcgg
fuente
2

Lo uso para buscar datos. Cargar una vez desde DB.

public sealed class APILookup
    {
        private static readonly APILookup _instance = new APILookup();
        private Dictionary<string, int> _lookup;

        private APILookup()
        {
            try
            {
                _lookup = Utility.GetLookup();
            }
            catch { }
        }

        static APILookup()
        {            
        }

        public static APILookup Instance
        {
            get
            {
                return _instance;
            }
        }
        public Dictionary<string, int> GetLookup()
        {
            return _lookup;
        }

    }
Manish Jain
fuente
2

Qué es un singleton:
es una clase que solo permite que se cree una instancia de sí mismo y, por lo general, proporciona acceso simple a esa instancia.

Cuándo debe usarlo:
depende de la situación.

Nota: no lo use en la conexión db, para obtener una respuesta detallada, consulte la respuesta de @Chad Grant

Aquí hay un ejemplo simple de Singleton:

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

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

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

También puedes usar Lazy<T>para crear tu Singleton.

Vea aquí un ejemplo más detallado usandoLazy<T>

MSTdev
fuente
1

Esto es lo que es singleton: http://en.wikipedia.org/wiki/Singleton_pattern

No sé C #, pero en realidad es lo mismo en todos los idiomas, solo la implementación difiere.

En general, debe evitar el singleton cuando sea posible, pero en algunas situaciones es muy conveniente.

Lo siento por mi ingles ;)

radex
fuente
tu inglés está bien :)
FrenkyB
1

La clase Singleton se utiliza para crear una instancia única para todo el dominio de la aplicación.

public class Singleton
{
    private static Singleton singletonInstance = CreateSingleton();

    private Singleton()
    {
    }

    private static Singleton CreateSingleton()
    {
        if (singletonInstance == null)
        {
            singletonInstance = new Singleton();
        }

        return singletonInstance;
    }

    public static Singleton Instance
    {
        get { return singletonInstance; }            
    }
}

En este artículo se describe cómo podemos crear clases singleton seguras para subprocesos utilizando variables de solo lectura y su uso práctico en aplicaciones.

Vikram
fuente
1

Sé que es muy tarde para responder la pregunta, pero con Auto-Property puedes hacer algo así:

public static Singleton Instance { get; } = new Singleton();

¿Dónde Singletonestá su clase y puede ser a través de, en este caso, la propiedad de solo lectura Instance?

Zyo
fuente
0

EX Puede usar Singleton para obtener información global que necesita ser inyectada.

En mi caso, estaba manteniendo los detalles del usuario registrado (nombre de usuario, permisos, etc.) en Global Static Class. Y cuando traté de implementar la Prueba de Unidad, no había forma de inyectar dependencia en las clases de Controlador. Por lo tanto, he cambiado mi clase estática al patrón Singleton.

public class SysManager
{
    private static readonly SysManager_instance = new SysManager();

    static SysManager() {}

    private SysManager(){}

    public static SysManager Instance
    {
        get {return _instance;}
    }
}

http://csharpindepth.com/Articles/General/Singleton.aspx#cctor

Gubi
fuente
0

Necesitamos usar el patrón de diseño Singleton en C # cuando necesitamos asegurarnos de que solo se creará una instancia de una clase en particular y luego proporcionar acceso global simple a esa instancia para toda la aplicación.

Escenarios en tiempo real donde puede usar el patrón de diseño Singleton: Proxies de servicio: como sabemos, invocar una API de servicio es una operación extensa en una aplicación. El proceso que lleva la mayor parte del tiempo es crear el cliente del Servicio para invocar la API del servicio. Si crea el proxy de servicio como Singleton, mejorará el rendimiento de su aplicación.

Fachadas: también puede crear las conexiones de la base de datos como Singleton, lo que puede mejorar el rendimiento de la aplicación.

Registros: en una aplicación, realizar la operación de E / S en un archivo es una operación costosa. Si crea su Logger como Singleton, mejorará el rendimiento de la operación de E / S.

Uso compartido de datos: si tiene valores constantes o valores de configuración, puede mantener estos valores en Singleton para que otros componentes de la aplicación puedan leerlos.

Almacenamiento en caché: como sabemos, recuperar los datos de una base de datos es un proceso lento. En su aplicación, puede almacenar en caché el maestro y la configuración en la memoria, lo que evitará las llamadas a la base de datos. En tales situaciones, la clase Singleton se puede usar para manejar el almacenamiento en caché con sincronización de subprocesos de una manera eficiente que mejora drásticamente el rendimiento de la aplicación.

Desventajas del patrón de diseño Singleton en C # Las desventajas de usar el patrón de diseño Singleton en C # son las siguientes:

La prueba unitaria es muy difícil porque introduce un estado global en una aplicación. Reduce el potencial de paralelismo dentro de un programa porque para acceder a la instancia de singleton en un entorno de subprocesos múltiples, debe serializar el objeto mediante el bloqueo.

He tomado esto del siguiente artículo.

https://dotnettutorials.net/lesson/singleton-design-pattern/

Pranaya Rout
fuente
0

Enhebre Safe Singleton sin usar bloqueos y sin instanciación perezosa.

Esta implementación tiene un constructor estático, por lo que se ejecuta solo una vez por dominio de aplicación.

public sealed class Singleton
{

    static Singleton(){}

    private Singleton(){}

    public static Singleton Instance { get; } = new Singleton();

}
Grigb
fuente