autor | |
---|---|
sitio web | https://github.com/lsauer/csharp-singleton |
licencia | Licencia de MIT |
actual | |
paquete | PM> Install-Package CSharp.Portable-Singleton |
descripción | Una implementación genérica, portátil, documentada y fácil de usar para la implementación de patrones de singleton, para hacer cumplir y administrar instancias individuales |
documentación | Referencia completa v2.0.0.4 |
subportado |
|
Versión completa | Nuget | Construir | Instalación nuget |
---|---|---|---|
Csharp.portable-singleton | PM> Install-Package CSharp.Portable-Singleton |
Social:
Visite aquí para una referencia completa, que también se incluye en el paquete Nuget.
PM> Install-Package CSharp.Portable-Singleton
.using Core.Singleton;
.MySingleton : Singleton<MySingleton>
.Encuentre a continuación un ejemplo para proporcionar una idea de cómo se verá el código en la práctica:
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 no hace cumplir particularmente los patrones de diseño de software. El patrón singleton es de uso notable en el software como un patrón de diseño creativo , en el que solo una instancia de un objeto puede ser instanciado, por lo que generalmente extiende la utilidad de los singletons a la creación o envoltura de recursos de acceso único.
La creación de un nuevo singleton es sencillo: declarar una herencia de la clase Singleton prevista a la clase Singleton Singleton<>
es suficiente.
Como:
internal class MyClass : Singleton < MyClass > {
.. .
}
Un ejemplo de uso para Singleton sería un envoltorio de consola mejorado para aplicaciones de consolas .NET, otros escenarios típicos serían tales donde el rendimiento y los aspectos de sincronización se ponen en consumo.
Nota: Posiblemente las aplicaciones a gran escala que se ejecutan en plataformas modernas pueden recurrir a soluciones mejoradas sobre singletons, particularmente a través del soporte de marco de patrones de diseño.
Para comenzar, se recomienda adherirse a la siguiente sintaxis:
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 ) ;
}
Hay varias otras formas de inicializar una nueva instancia Singleton<T>
, en la que T
es el tipo de clase lógica respectiva, que se refiere a la clase que implementa la lógica personalizada.
Singleton<T>.CurrentInstance
o Singleton<T>.Instance
por primera veznew T()
SingletonAttribute
como [Singleton]class T : Singleton<T>{...}
y posteriormente llamando Initialize()
desde una instancia Singletonmanager
Activator.CreateInstance(typeof(T));
new T(...)
SingletonManager
(ver más abajo)TypeInfo
ToSingleton()
por typeof(MyClass).GetTypeInfo().ToSingleton()
Examples
de escenarios de código y casos La construcción genérica Singleton<T>
tiene las siguientes propiedades estáticas, a las que se hace referencia en 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 ,
En casos especiales, la eliminación es útil o incluso necesaria. Vea los ejemplos para los casos.
myobj is ISingleton
typeof(MyClass).GetTypeInfo().IsSingleton()
Respectivamente, omita la llamada a GetTypeInfo()
como se muestra anteriormente, si el tipo de comparación ya es una instancia TypeInfo
.
(Instance == null)
Las siguientes propiedades siguen la convención de INotifyPropertyChanged
pero no la implementan, mientras usa un SingletonPropertyEventHandler
personalizado en lugar de la PropertyChangedEventHandler
cañana ChangedEventHandler.
El evento PropertyChanged
se declara estático para permitir que la escucha sea Disposed
e Initialized
incluso cuando la instancia de Singleton está dispuesta y libre para la recolección de basura.
public static event SingletonEventHandler PropertyChanged ;
Además, se activa un evento cuando cambia el Manager
de la propiedad. Esta propiedad se utiliza para la inyección de dependencia del setter de una instancia de SingletonManager que implementa ISingletonManager
.
En el caso de varias clases de Singleton en un proyecto determinado, se recomienda usar y pasar una instancia SingletonManager
.
Por ejemplo, para escuchar el evento Disposed
para las tareas posteriores a la limpieza, durante el cierre o la salida de una aplicación, uno puede usar una muestra de código similar de la siguiente manera:
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 ( ) ;
Tenga en cuenta que el singleton ni siquiera tiene que ser inicializado en este punto, lo que hace que sea seguro intializar elementos IStream
típicos dentro del constructor Singleton.
El EventHandler de PropertyChanged
pasa una instancia de ISingleton
como el primer argumento, y como segundo parámetro una instancia de SingletonPropertyEventArgs
, que contiene las siguientes propiedades:
Name
: una cadena que contiene el nombre de la propiedad cambiadaValue
: el valor actual en caja de la propiedadProperty
: la propiedad codificada como un valor enum de SingletonProperty
El siguiente código de código crea una nueva instancia SingletonPropertyEventArgs
:
var propertyName = SingletonProperty . Instance . ToString ( ) ;
var propertyValue = 100 ;
var args = new SingletonPropertyEventArgs ( SingletonProperty . Initialized , propertyValue ) ;
El siguiente ejemplo demuestra el uso dinámico de GetValue
dentro de un EventHandler, para acceder a las propiedades de Singleton no conocidas hasta el tiempo de ejecución.
Singelton < MyClass > . PropertyChanged += ( sender , arg ) =>
{
if ( arg . Property == SingletonProperty . Initialized )
{
var value = sender . GetValue ( " Value " ) ;
}
} ;
En general, se recomienda a las propiedades de ACCSS de singletons similares a través de interfaces personalizadas (es decir, ISingletonTemplate<TCommonDenominator>
) y realice typechecks específicos utilizando el operador is
junto a canas explícitas:
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
}
} ;
En el siguiente ejemplo, la clase AClass
implementa la 'lógica de negocios de Singleton' y hereda de Singleton<>
.
Es suficiente incluir los ensamblados, espacios de nombres y derivación : Singleton<AClass>
para obtener el comportamiento probado esperado:
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: Se proporcionan muchos más ejemplos en su totalidad, dentro de la carpeta de ejemplos.
Este ejemplo anterior producirá el resultado esperado de:
Running: Examples.Example1. Press any key to quit...
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Una clase singleton puede lanzar una SingletonException
(ver Fig. 1).
Estos se hacen referencia en 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 la inicialización global, así como la constricción, el propósito de un singleton, la clase lógica Singleton siempre debe atribuirse con [Singleton]
como se muestra en el siguiente ejemplo del código:
[ Singleton ( disposable : false , initByAttribute : false , createInternal : true ) ]
public class AClass : Singleton < AClas > {
.. .
}
El atributo tiene tres propiedades accesibles:
Disposable
(predeterminado = falso): establecer en true
si se permite que se elimineCreateInternal
(default = true): Establecer en false
si se supone que el singleton solo debe ser instanciado externamente mediante una declaración explícita dentro del código de origen del usuarioInitByAttribute
(default = true): Establecer en true
para permitir la inicialización conjunta por el método SingletonManager
Initialize
Para administrar varios tipos e instancias de Singleton a lo largo de una gran aplicación, use la clase SingletonManager
de la siguiente manera:
El siguiente ejemplo itera sobre un Pool
de singletons y realiza la lógica que depende del 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);
}
}
La propiedad singletonManager.Pool
proporciona acceso a una instancia de hilo, ConcurrentDictionary<Type, ISingleton>
que permite escribir consultas en la sintaxis de LINQ familiar.
Los singletones dispuestos nunca se eliminan, pero están configurados en null
utilizando el método AddOrUpdate
del SingletonManager.
Para crear nuevas instancias de un tipo conocido, use el método genérico CREATInStance de la siguiente manera:
var singletonManager = new SingletonManager ( ) ;
var gameStatics = singletonManager . CreateSingleton < GameStatics > ( ) ;
Si el tipo solo se conoce en tiempo de ejecución o está disponible, pase dinámicamente el tipo como argumento, como se muestra en el siguiente ejemplo de código:
var singletonManager = new SingletonManager ( ) ;
var getInstance = ( type ) => {
var gameStatics = singletonManager . CreateSingleton ( type ) ;
} ;
getInstance ( typeof ( GameStatics ) ) ;
No hay nada en la clase Singleton que evite la implementación de un serializador, sin embargo, la implementación y las pruebas están en manos del desarrollador.
Las soluciones genéricas no se recomiendan, sino que son implementaciones probadas de esos singletons cuando sea necesario. Por ejemplo, en un escenario de estado de hibernación / currículum. Se recomienda usar Extender el SingletonManager para ese propósito.
También eche un vistazo a esta discusión
Esta biblioteca ha sido probada por el marco de pruebas XUnit. Las pruebas se ejecutan con varias clases, una de AClass
que se adhiere a un esquema de herencia cañana directa:
Fig. 1:
Si está seguro de que se encontró con un error, presione un nuevo problema aquí.
En un escenario de herencia anidada de varias clases que se heredan jerárquicamente, y una clase base determinada que se deriva de Singleton, es importante definir la clase lógica de singleton. Esta es la clase destinada a implementar la lógica de la solaTn siguiendo la responsabilidad única Pricipal.
También es la clase la que determina el tipo T
genérico de la que la clase base, la clase de menos de Singleton<T>
en sí, debe heredar Singleton<T>
Para un escenario de herencia más complejo, consulte README_Example_Advanced.md
Para mantener una buena legibilidad al usar esta biblioteca:
singleton<T>
: por ejemplo, ParentOfParentOfAClass.Instance
está bien, pero evite AClass.Instance
SingletonAttribute