auteur | |
---|---|
site web | https://github.com/lsauer/csharp-singleton |
licence | Licence MIT |
actuel | |
emballer | PM> Install-Package CSharp.Portable-Singleton |
description | Une implémentation générique, portable, documentée et facile à utiliser |
documentation | Référence complète v2.0.0.4 |
soutenu |
|
Version complète | Nuget | Construire | Installation de Nuget |
---|---|---|---|
CSharp.portable-Singleton | PM> Install-Package CSharp.Portable-Singleton |
Sociale:
Veuillez visiter ici pour une référence complète, qui est également incluse dans le package NuGet.
PM> Install-Package CSharp.Portable-Singleton
.using Core.Singleton;
.MySingleton : Singleton<MySingleton>
.Trouvez ci-dessous un exemple pour donner un aperçu de ce à quoi ressemblera le code dans la pratique:
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'applique pas particulièrement les modèles de conception de logiciels. Le modèle singleton est d'une utilisation notable dans le logiciel comme modèle de conception de création , dans lequel une seule instance d'un objet peut être instanciée, étendant ainsi généralement l'utilité des singletons à la création ou en emballage des ressources à accès unique.
La création d'un nouveau singleton est simple: déclarer un héritage de la classe Singleton prévue à la classe générique de Singleton Singleton<>
suffit.
Tel que:
internal class MyClass : Singleton < MyClass > {
.. .
}
Un exemple d'utilisation pour les singletons serait un emballage console amélioré pour les applications de console .NET, d'autres scénarios typiques seraient tels que des performances et des aspects de synchronisation sont mis à l'honneur.
Remarque: Les applications sans doute à grande échelle exécutées sur des plates-formes modernes peuvent recourir à des solutions améliorées sur des singletons, en particulier grâce à la prise en charge des modèles de conception.
Pour commencer, il est recommandé d'adhérer à la syntaxe suivante:
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 ) ;
}
Il existe plusieurs autres façons d'initialiser une nouvelle instance Singleton<T>
, dans T
est le type de classe Singleton logique respective, faisant référence à la classe implémentant la logique personnalisée.
Singleton<T>.CurrentInstance
ou Singleton<T>.Instance
pour la première foisnew T()
SingletonAttribute
tels que [Singleton]class T : Singleton<T>{...}
et par la suite appeler Initialize()
à partir d'une instance Singletonmanager
Activator.CreateInstance(typeof(T));
new T(...)
SingletonManager
(voir ci-dessous)TypeInfo
ToSingleton()
par exemple typeof(MyClass).GetTypeInfo().ToSingleton()
Examples
de code et de scénarios de cas La construction générique Singleton<T>
a les propriétés statiques suivantes, qui sont référencées dans 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 ,
Dans des cas particuliers, l'élimination est utile ou même nécessaire. Voir les exemples des cas.
myobj is ISingleton
typeof(MyClass).GetTypeInfo().IsSingleton()
Respectivement, omettez l'appel à GetTypeInfo()
comme indiqué ci-dessus, si le type de comparaison est déjà une instance TypeInfo
.
(Instance == null)
Les propriétés suivantes suivent la convention d' INotifyPropertyChanged
mais ne la mettent pas en œuvre, tout en utilisant un SingletonPropertyEventHandler
typlé personnalisé au lieu du PropertyChangedEventHandler
de la propriété cannonique.
L' événement PropertyChanged
lui-même est déclaré statique pour permettre à l'écoute de Disposed
et Initialized
même lorsque l'instance singleton elle-même est éliminée et gratuite pour la collecte des ordures.
public static event SingletonEventHandler PropertyChanged ;
De plus, un événement est déclenché lorsque le Manager
immobilier change. Cette propriété est utilisée pour l'injection de dépendance du secteur d'une instance singletonManager implémentant ISingletonManager
.
Dans le cas de plusieurs classes singleton dans un projet donné, il est recommandé d'utiliser et de faire le passage d'une instance SingletonManager
.
Par exemple, pour écouter l'événement Disposed
pour les tâches post-nettoyage, lors de la fermeture ou de la sortie d'une application, on peut utiliser un échantillon de code similaire à celle:
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 ( ) ;
Notez que le singleton n'a même pas besoin d'être initialisé à ce stade, ce qui rend sécurisé les éléments IStream
typiques intimes au sein du constructeur de Singleton.
Le Handleur Event de PropertyChanged
passe une instance d' ISingleton
comme premier argument, et en tant que deuxième paramètre, une instance de SingletonPropertyEventArgs
, qui contient les propriétés suivantes:
Name
: une chaîne contenant le nom de la propriété modifiéeValue
: la valeur actuelle en boîte de la propriétéProperty
: la propriété codée comme une valeur d'énumération de SingletonProperty
L'extrait de code suivant crée une nouvelle instance SingletonPropertyEventArgs
:
var propertyName = SingletonProperty . Instance . ToString ( ) ;
var propertyValue = 100 ;
var args = new SingletonPropertyEventArgs ( SingletonProperty . Initialized , propertyValue ) ;
L'exemple suivant démontre l'utilisation dynamique de GetValue
dans un EventHandler, pour accéder aux propriétés de Singleton qui ne sont pas connues avant l'exécution.
Singelton < MyClass > . PropertyChanged += ( sender , arg ) =>
{
if ( arg . Property == SingletonProperty . Initialized )
{
var value = sender . GetValue ( " Value " ) ;
}
} ;
Généralement, il est recommandé aux propriétés ACCSS de singletons similaires via des interfaces personnalisées (c'est-à-dire ISingletonTemplate<TCommonDenominator>
) et effectuer des types spécifiques à l'aide de l'opérateur is
aux côtés de moulages explicites:
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
}
} ;
Dans l'exemple suivant, la classe AClass
implémente la «Singleton Business Logic» et hérite de Singleton<>
.
Il suffit d'inclure les assemblages, les espaces de noms et la dérivation : Singleton<AClass>
pour obtenir le comportement testé et testé:
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 ) ;
}
Remarque: De nombreux autres exemples sont fournis en entier, dans le dossier Exemples.
Cet exemple ci-dessus donnera le résultat attendu de:
Running: Examples.Example1. Press any key to quit...
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Une classe singleton peut lancer une SingletonException
(voir figure 1).
Ceux-ci sont référencés dans 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 ,
Pour l'initialisation globale ainsi que la constriction du but d'un singleton, la classe logique singleton doit toujours être attribuée avec [Singleton]
comme indiqué dans l'exemple de code suivant:
[ Singleton ( disposable : false , initByAttribute : false , createInternal : true ) ]
public class AClass : Singleton < AClas > {
.. .
}
L'attribut a trois propriétés accessibles:
Disposable
(default = false): réglé sur true
si il est autorisé à être éliminéCreateInternal
(default = true): réglé sur false
si le singleton n'est censé être instancié en externe par déclaration explicite dans le code source utilisateurInitByAttribute
(default = true): réglé sur true
pour permettre l'initialisation conjointe par la méthode SingletonManager
Initialize
Pour gérer plusieurs types et instances singleton dans toute une grande application, utilisez la classe SingletonManager
comme suit:
L'exemple suivant itère dans un Pool
de singletons et interprète la logique dépendante du type 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 propriété singletonManager.Pool
donne accès à une instance de filetage, ConcurrentDictionary<Type, ISingleton>
qui permet d'écrire des requêtes dans la syntaxe Linq familière.
Les singletons disposés ne sont jamais supprimés mais sont définis pour null
en utilisant la méthode AddOrUpdate
de SingletonManager.
Pour créer de nouvelles instances d'un type connu, utilisez la méthode de création générique comme suit:
var singletonManager = new SingletonManager ( ) ;
var gameStatics = singletonManager . CreateSingleton < GameStatics > ( ) ;
Si le type n'est connu qu'à l'exécution ou disponible, passez dynamiquement le type comme argument, comme indiqué dans l'exemple de code suivant:
var singletonManager = new SingletonManager ( ) ;
var getInstance = ( type ) => {
var gameStatics = singletonManager . CreateSingleton ( type ) ;
} ;
getInstance ( typeof ( GameStatics ) ) ;
Il n'y a rien dans la classe Singleton lui-même qui empêcherait la mise en œuvre d'un sérialiseur, mais la mise en œuvre ainsi que les tests sont entre les mains du développeur.
Les solutions génériques ne sont pas recommandées mais plutôt des implémentations testées spécifiques de ces singletons si nécessaire. Par exemple dans un scénario d'état Hibernate / CV. Il est recommandé d'utiliser étendre le singletonManager à cette fin.
Jetez également un œil à cette discussion
Cette bibliothèque a été testée par le cadre de test Xunit. Les tests sont exécutés avec plusieurs classes, une avec AClass
adhérant à un schéma de succession canonique simple:
Fig 1:
Si vous êtes certain que vous avez rencontré un bug, veuillez pousser un nouveau problème ici.
Dans un scénario d'héritage imbriqué de plusieurs classes héritant les uns des autres hiérarchiquement et une classe de base déterminée dérivée de Singleton, il est important de définir la classe Singleton logique. Il s'agit de la classe destinée à implémenter la logique du singleoTn suivant le pricicipal de responsabilité unique.
Il est également la classe qui détermine le type générique T
à partir de laquelle la classe de base - la classe à court de Singleton<T>
elle-même, doit hériter Singleton<T>
Pour un scénario de singleton d'héritage plus complexe, veuillez vous référer à README_Example_Advanced.md
Pour maintenir une bonne lisibilité lors de l'utilisation de cette bibliothèque:
singleton<T>
: par exemple, ParentOfParentOfAClass.Instance
est OK, mais évitez AClass.Instance
SingletonAttribute