autor | |
---|---|
site | https://github.com/lsauer/csharp-singleton |
licença | MIT Licença |
atual | |
pacote | PM> Install-Package CSharp.Portable-Singleton |
descrição | Uma implementação genérica, portátil, documentada e fácil de usar, para aplicar e gerenciar instâncias únicas |
documentação | Referência completa v2.0.0.4 |
suportado |
|
Versão completa | NUGET | Construir | Nuget Install |
---|---|---|---|
Csharp.portable-singleton | PM> Install-Package CSharp.Portable-Singleton |
Social:
Visite aqui para uma referência completa, que também está incluída no pacote Nuget.
PM> Install-Package CSharp.Portable-Singleton
.using Core.Singleton;
.MySingleton : Singleton<MySingleton>
.Encontre abaixo um exemplo para fornecer um vislumbre de como será o código na prática:
using Core . Singleton ;
public class AClass : Singleton < AClass >
{
// a public parameterless constructor is required
public AClass ( ) { }
public AMethod ( ) { Console . Write ( " Write called " ) ; }
}
AClass . CurrentInstance . AMethod ( ) ;
System . Diagnostics . Debug . Assert ( ReferenceEquals ( new AClass ( ) , AClass . CurrentInstance ,
" Same Instance " ) ;
.NET não aplica particularmente padrões de design de software. O padrão Singleton é de uso notável no software como um padrão de design criativo , em que apenas uma instância de um objeto pode ser instanciada, geralmente ampliando a utilidade dos singletons para a criação ou embrulho de recursos de acesso único.
Criar um novo singleton é direto: declarar uma herança da classe singleton pretendida para a classe genérica Singleton Singleton<>
Basta.
Como:
internal class MyClass : Singleton < MyClass > {
.. .
}
Um exemplo de uso para singletons seria um invólucro de console aprimorado para aplicativos de console .NET, outros cenários típicos seriam os aspectos de desempenho e sincronização.
NOTA: Os aplicativos indiscutivelmente em larga escala em execução em plataformas modernas podem recorrer a soluções aprimoradas sobre singletons, particularmente através do suporte à estrutura dos padrões de design.
Para começar, é recomendável aderir à seguinte sintaxe:
namespace MyNamespace {
using Core . Singleton ;
public class MyClass : Singleton < MyClass > { } ;
var somePropertyValue = Singleton < MyClass > . CurrentInstance . SomeProperty ;
// ...and for a method:
var someMethodValue = Singleton < MyClass > . CurrentInstance . Add ( 1 , 2 ) ;
}
Existem várias outras maneiras de inicializar uma nova instância Singleton<T>
, em que T
é o tipo da respectiva classe lógica singleton, referindo -se à classe que implementa a lógica personalizada.
Singleton<T>.CurrentInstance
ou Singleton<T>.Instance
pela primeira veznew T()
SingletonAttribute
, como [Singleton]class T : Singleton<T>{...}
e subsequentemente chamando Initialize()
de uma instância Singletonmanager
Activator.CreateInstance(typeof(T));
new T(...)
SingletonManager
(veja abaixo)TypeInfo
ToSingleton()
por exemplo typeof(MyClass).GetTypeInfo().ToSingleton()
Examples
de cenários de código e caso O construto genérico Singleton<T>
possui as seguintes propriedades estáticas, que são referenciadas em EnumSingletonProperty.cs
:
[ Description ( " The current or created instance of the singleton " ) ]
CurrentInstance = 1 << 1 ,
[ Description ( " The internally created instance of the singleton " ) ]
Instance = 1 << 2 ,
[ Description ( " Gets whether the singleton of type TClass is initialized " ) ]
Initialized = 1 << 3 ,
[ Description ( " Gets whether the singleton of type TClass is disposed " ) ]
Disposed = 1 << 4 ,
[ Description ( " Gets whether the singleton of type TClass is blocked for handling " ) ]
Blocked = 1 << 5 ,
Em casos especiais, o descarte é útil ou mesmo necessário. Veja os exemplos para casos.
myobj is ISingleton
typeof(MyClass).GetTypeInfo().IsSingleton()
Respectivamente, omite a chamada para GetTypeInfo()
como mostrado acima, se o tipo de comparação já for uma instância TypeInfo
.
(Instance == null)
As propriedades a seguir seguem a Convenção do INotifyPropertyChanged
, mas não a implementam, usando um SingletonPropertyEventHandler
digitado personalizado em vez do PropertyChangedEventHandler
Cannonical.
O próprio evento PropertyChanged
é declarado estático para permitir que a escuta seja Disposed
e Initialized
mesmo quando a própria instância singleton é descartada e gratuita para a coleta de lixo.
public static event SingletonEventHandler PropertyChanged ;
Além disso, um evento é acionado quando o Manager
da propriedade muda. Esta propriedade é usada para injeção de dependência do setter de uma instância de singletonmanager implementando ISingletonManager
.
No caso de várias classes de singleton em um determinado projeto, é recomendável usar e passar uma instância SingletonManager
.
Por exemplo, para ouvir o evento Disposed
para tarefas pós-limpeza, durante o desligamento ou a saída de um aplicativo, pode-se usar uma amostra de código semelhante da seguinte forma:
Singleton < MyClass > . PropertyChanged += ( sender , arg ) => {
if ( arg . Property == SingletonProperty . Disposed ) {
.. .
}
.. .
} ;
//... prep the application until it is sensible to init the singleton
var logger = Singleton < RenderLogger > . GetInstance ( ) ;
Observe que o singleton nem precisa ser inicializado neste momento, tornando seguro a intializar elementos típicos IStream
dentro do construtor singleton.
O EventHandler de PropertyChanged
passa uma instância de ISingleton
como o primeiro argumento e como segundo parâmetro Uma instância de SingletonPropertyEventArgs
, que contém as seguintes propriedades:
Name
: Uma string contendo o nome da propriedade alteradaValue
: o valor atual em caixa da propriedadeProperty
: a propriedade codificada como um valor de enumeração da SingletonProperty
O trecho de código a seguir cria uma nova instância SingletonPropertyEventArgs
:
var propertyName = SingletonProperty . Instance . ToString ( ) ;
var propertyValue = 100 ;
var args = new SingletonPropertyEventArgs ( SingletonProperty . Initialized , propertyValue ) ;
O exemplo a seguir demonstra o uso dinâmico do GetValue
em um EventHandler, para acessar as propriedades Singleton não conhecidas até o tempo de execução.
Singelton < MyClass > . PropertyChanged += ( sender , arg ) =>
{
if ( arg . Property == SingletonProperty . Initialized )
{
var value = sender . GetValue ( " Value " ) ;
}
} ;
Geralmente, é recomendável as propriedades do ACCSS de singletons semelhantes por meio de interfaces personalizadas (ou seja, ISingletonTemplate<TCommonDenominator>
) e executem um TypeChecks específicos usando o operador is
ao lado de elenco explícito:
Singelton < MyClass > . PropertyChanged += ( sender , arg ) =>
{
if ( arg . Property == SingletonProperty . Initialized )
{
if ( sender is MyClass /*check including inherited types*/ ) {
var senderTyped = sender as MyClass ;
senderTyped . SetDateTime ( DateTime . Now ) ;
} else if ( sender . GetType ( ) == typeof ( MyStrictClass ) /*check excluding inherited types*/ ) {
var senderTyped = sender as MyStrictClass ;
Console . WriteLine ( senderTyped . SayHello ( ) ) ;
} else {
return ;
}
// do something else if the type got matched
}
} ;
No exemplo seguinte, a classe AClass
implementa a 'lógica de negócios singleton' e herda de Singleton<>
.
Basta incluir os assemblies, espaços para nome e derivação : Singleton<AClass>
para obter o comportamento esperado e testado:
using Core . Extensions .
public class AClass : Singleton < AClass >
{
public string AMethod ( [ CallerMemberName ] string caller = " " )
{
return caller ;
}
public static string AStaticMethod ( [ CallerMemberName ] string caller = " " )
{
return caller ;
}
}
static void Main ( string [ ] args )
{
Console . WriteLine ( " Running: " + typeof ( Program ) . Namespace + " . Press any key to quit... " ) ;
var aClass = new AClass ( ) ;
Console . WriteLine ( " Expected: 'Main'; Observed: '{0}' " , aClass . AMethod ( ) ) ;
Console . WriteLine ( " Expected: 'Main'; Observed: '{0}' " , AClass . CurrentInstance . AMethod ( ) ) ;
Console . WriteLine ( " Expected: 'Main'; Observed: '{0}' " , AClass . AStaticMethod ( ) ) ;
object bClass = null ;
try
{
bClass = new AClass ( ) ;
}
catch ( SingletonException exc )
{
if ( exc . Cause == SingletonCause . InstanceExists )
bClass = AClass . CurrentInstance ;
}
var condition = Object . ReferenceEquals ( aClass , bClass ) ;
//> true
var input = Console . ReadKey ( true ) ;
}
Nota: muitos outros exemplos são fornecidos integralmente, dentro da pasta Exemplos.
Este exemplo acima produzirá o resultado esperado de:
Running: Examples.Example1. Press any key to quit...
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Uma aula de singleton pode lançar uma SingletonException
(veja a Fig. 1).
Estes são referenciados em EnumSingletonCause.cs
.
[ Description ( " Indicates the default or unspecified value " ) ]
Unknown = 1 << 0 ,
[ Description ( " Indicates an existing Singleton instance of the singleton class `T` " ) ]
InstanceExists = 1 << 1 ,
[ Description ( " Indicates that the created Singleton instance does not have a parent class " ) ]
NoInheritance = 1 << 2 ,
[ Description ( " Indicates that an exception by another class or module was caught " ) ]
InternalException = 1 << 3 ,
[ Description ( " Indicates that the Singleton must not be instanced lazily through an Acccessor, but the instance explcitely declared in the source-code " ) ]
NoCreateInternal = 1 << 4 ,
[ Description ( " Indicates that the Singleton must not be disposed " ) ]
NoDispose = 1 << 5 ,
[ Description ( " Indicates an existing mismatch between the singleton class `T` and the logical singleton class or parent-class invoking the constructor " ) ]
InstanceExistsMismatch = 1 << 6 ,
Para inicialização global e constrição, o objetivo de um singleton, a classe lógica de singleton deve sempre ser atribuída com [Singleton]
como mostrado no exemplo de código a seguir:
[ Singleton ( disposable : false , initByAttribute : false , createInternal : true ) ]
public class AClass : Singleton < AClas > {
.. .
}
O atributo possui três propriedades acessíveis:
Disposable
(padrão = false): definido como true
se for permitido ser descartadoCreateInternal
(padrão = true): defina como false
se o singleton deve ser instanciado externamente por declaração explícita dentro do código de origem do usuárioInitByAttribute
(padrão = true): defina como true
para permitir a inicialização conjunta pelo método SingletonManager
Initialize
Para gerenciar vários tipos e instâncias de singleton em um grande aplicativo, use a classe SingletonManager
da seguinte maneira:
O exemplo a seguir itera sobre um Pool
de singletons e executa a lógica dependendo do tipo de singleton:
var singletonTypes = new List<Type>() { typeof(ParentOfParentOfAClass), typeof(ParentOfAClass), typeof(IndispensibleClass) };
// create the singletons and add them to the manager
var singletonManager = new SingletonManager(singletonTypes);
foreach (var singleton in singletonManager.Pool)
{
if (singleton.Value is ParentOfParentOfAClass)
{
var instanceTyped = singleton.Value as ParentOfParentOfAClass;
Console.WriteLine($"POPOAClass ImplementsLogic: {instanceTyped.ImplementsLogic}");
} else {
Console.WriteLine(singleton.Value.GetType().FullName);
}
}
A propriedade singletonManager.Pool
fornece acesso a uma instância de um threads, ConcurrentDictionary<Type, ISingleton>
que permite gravar consultas na sintaxe do LINQ Familiar.
Os singletons descartados nunca são excluídos, mas estão definidos como null
usando o método AddOrUpdate
do SingletonManager.
Para criar novas instâncias de um tipo conhecido, use o método Genérico CreateInstance da seguinte maneira:
var singletonManager = new SingletonManager ( ) ;
var gameStatics = singletonManager . CreateSingleton < GameStatics > ( ) ;
Se o tipo for conhecido apenas em tempo de execução ou disponível dinamicamente o tipo como argumento, conforme mostrado no exemplo de código a seguir:
var singletonManager = new SingletonManager ( ) ;
var getInstance = ( type ) => {
var gameStatics = singletonManager . CreateSingleton ( type ) ;
} ;
getInstance ( typeof ( GameStatics ) ) ;
Não há nada na classe singleton que impedisse a implementação de um serializador, no entanto, a implementação e o teste estão nas mãos do desenvolvedor.
Soluções genéricas não são recomendadas, mas sim implementações específicas testadas daqueles singletons, quando necessário. Por exemplo, em um cenário de estado de Hibernate / retomar. Recomenda -se usar estender o SingletonManager para esse fim.
Também dê uma olhada nesta discussão
Esta biblioteca foi testada pela estrutura de teste XUnit. Os testes são executados com várias classes, uma com adesão AClass
a um esquema de herança canônica direta:
Fig 1:
Se você tiver certeza de que encontrou um bug, por favor, pressione um novo problema aqui.
Em um cenário de herança aninhada de várias classes que se herdam hierarquicamente e uma classe base determinada derivada de Singleton, é importante definir a classe lógica de singleton. Esta é a classe destinada a implementar a lógica do singetn após a única responsabilidade.
É também a classe que determina o tipo genérico T
a partir do qual a classe base - a classe curta do próprio Singleton<T>
, deve herdar Singleton<T>
Para um cenário de herança mais complexo, consulte README_Example_Advanced.md
Para manter uma boa legibilidade ao usar esta biblioteca:
singleton<T>
: por exemplo, ParentOfParentOfAClass.Instance
está ok, mas evite AClass.Instance
SingletonAttribute