Padrão Singleton para C# [fechado]
8 answers
Tipicamente um singleton não é Uma classe estática - um singleton dar-lhe-á uma única instância de uma classe.
Não sei que exemplos você viu, mas normalmente o padrão de singleton pode ser muito simples em C#:
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
static Singleton() {} // Make sure it's truly lazy
private Singleton() {} // Prevent instantiation outside
public static Singleton Instance { get { return instance; } }
}
Isso não é difícil.
A vantagem de um singleton sobre os membros estáticos é que a classe pode implementar interfaces, etc. Às vezes isso é útil - mas outras vezes, membros estáticos realmente faria bem. Além disso, é geralmente mais fácil passar de um singleton para um não-singleton mais tarde, por exemplo, passando no singleton como um objeto de "configuração" para classes de dependência, em vez daquelas classes de dependência que fazem chamadas estáticas diretas.
Pessoalmente, tentaria evitar usar singletons sempre que possível, tornando os testes mais difíceis, além de qualquer outra coisa. Podem ocasionalmente ser úteis.
Registo
struct Data {
public String ProgramName;
public String Parameters;
}
class FooRegistry {
private static Dictionary<String, Data> registry = new Dictionary<String, Data>();
public static void Register(String key, Data data) {
FooRegistry.registry[key] = data;
}
public static void Get(String key) {
// Omitted: Check if key exists
return FooRegistry.registry[key];
}
}
Vantagens
- É fácil mudar para um objecto falso para testes automáticos.
- você ainda pode armazenar várias instâncias, mas se necessário você tem apenas uma instância.
Desvantagens
- ligeiramente mais lento do que um Singleton ou uma variável global
Estática Classe
class GlobalStuff {
public static String ProgramName {get;set;}
public static String Parameters {get;set;}
private GlobalStuff() {}
}
Vantagens
- simples
- Rápido
Desvantagens
- É difícil mudar dinamicamente para um objecto falso.
- difícil mudar para outro tipo de objecto se os requisitos mudarem
Singleton Simple
class DataSingleton {
private static DataSingleton instance = null;
private DataSingleton() {}
public static DataSingleton Instance {
get {
if (DataSingleton.instance == null) DataSingleton.instance = new DataSingleton();
return DataSingleton;
}
}
}
Vantagens
- Nenhum realmente
Desvantagens
- É difícil criar uma threadsafe singleton., a versão acima irá falhar se vários tópicos acederem à instância.
- é difícil mudar para um objecto falso
Você deve dar uma olhada na injeção de dependência como geralmente é considerada a melhor prática, mas é um tópico muito grande para explicar aqui:
public sealed class Singleton
{
Singleton _instance = null;
public Singleton Instance
{
get
{
if(_instance == null)
_instance = new Singleton();
return _instance;
}
}
// Default private constructor so only we can instanctiate
private Singleton() { }
// Default private static constructor
private static Singleton() { }
}
Se vais seguir o caminho que estás a pensar, uma estática selada a aula vai funcionar bem.
Usando Inicializadores De Propriedade Automática C# 6.
public sealed class Singleton
{
private Singleton() { }
public static Singleton Instance { get; } = new Singleton();
}
Curto e limpo, ficarei feliz em ouvir as desvantagens.
Eu sei que este problema é antigo, mas aqui está outra solução usando. Net 4.0 ou mais tarde (incluindo.Net Core e .net Standard).
Primeiro, define a tua classe que será transformada num Singleton.public class ClassThatWillBeASingleton
{
private ClassThatWillBeASingleton()
{
Thread.Sleep(20);
guid = Guid.NewGuid();
Thread.Sleep(20);
}
public Guid guid { get; set; }
}
Nesta classe de exemplo, defini um construtor que dorme durante algum tempo, e depois cria um novo Guid e guarda para a sua propriedade pública. (O sono é apenas para testes de concorrência)
Repare que o construtor é privado, para que ninguém possa criar um um novo exemplo desta turma.
Temos de definir o invólucro que transformará esta classe num singleton.
public abstract class SingletonBase<T> where T : class
{
private static readonly Lazy<T> _Lazy = new Lazy<T>(() =>
{
// Get non-public constructors for T.
var ctors = typeof(T).GetConstructors(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
if (!Array.Exists(ctors, (ci) => ci.GetParameters().Length == 0))
throw new InvalidOperationException("Non-public ctor() was not found.");
var ctor = Array.Find(ctors, (ci) => ci.GetParameters().Length == 0);
// Invoke constructor and return resulting object.
return ctor.Invoke(new object[] { }) as T;
}, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
public static T Instance
{
get { return _Lazy.Value; }
}
}
Repare que ele usa a preguiça para criar um campo _Lazy
que sabe como instanciar uma classe usando o seu construtor privado.
E define uma propriedade Instance
para aceder ao valor do campo preguiçoso.
ExecutionAndPublication
. Assim, apenas um tópico será permitido inicializar o Valor do campo preguiçoso.
Agora, tudo o que temos de fazer é definir a classe embrulhada que será uma singleton:
public class ExampleSingleton : SingletonBase<ClassThatWillBeASingleton>
{
private ExampleSingleton () { }
}
Aqui está um exemplo do uso:
ExampleSingleton.Instance.guid;
E um teste para afirmar que dois fios terão a mesma instância do Singleton:
[Fact()]
public void Instance_ParallelGuid_ExpectedReturnSameGuid()
{
Guid firstGuid = Guid.Empty;
Guid secondGuid = Guid.NewGuid();
Parallel.Invoke(() =>
{
firstGuid = Singleton4Tests.Instance.guid;
}, () =>
{
secondGuid = Singleton4Tests.Instance.guid;
});
Assert.Equal(firstGuid, secondGuid);
}
Este teste está chamando o valor do campo preguiçoso simultaneamente, e queremos afirmar que ambas as instâncias que serão devolvidas desta propriedade (valor de preguiçoso) são as mesmas.
Mais detalhes sobre este assunto pode ser encontrado em: C# em profundidade
Http://blueonionsoftware.com/blog.aspx?p=c6e72c38-2839-4696-990a-3fbf9b2b0ba4
No entanto, sugiro que os singletons são padrões muito feios... Considero-os um anti-padrão.Http://blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx
Para mim, prefiro ter algo como ... Repositório, implementando IRepository. A sua classe pode declarar a dependência do IRepository no construtor e pode ser aprovada através de uma injecção de dependência ou de um destes métodos:Usa as tuas características linguísticas. A implementação mais simples de thread-safe é:
public sealed class Singleton
{
private static readonly Singleton _instance;
private Singleton() { }
static Singleton()
{
_instance = new Singleton();
}
public static Singleton Instance
{
get { return _instance; }
}
}
...qual seria a classe singleton mais simples?
Só para adicionar mais uma solução possível. A abordagem mais simples, mais direta e fácil de usar que eu possa pensar seria algo assim:
//The abstract singleton
public abstract class Singleton<T> where T : class
{
private static readonly Lazy<T> instance = new Lazy<T>( CreateInstance, true );
public static T Instance => instance.Value;
private static T CreateInstance()
{
return (T)Activator.CreateInstance( typeof(T), true);
}
}
//This is the usage for any class, that should be a singleton
public class MyClass : Singleton<MyClass>
{
private MyClass()
{
//Code...
}
//Code...
}
//Example usage of the Singleton
class Program
{
static void Main(string[] args)
{
MyClass clazz = MyClass.Instance;
}
}