Autor | |
---|---|
Webseite | https://github.com/lsauer/csharp-singleton |
Lizenz | MIT -Lizenz |
aktuell | |
Paket | PM> Install-Package CSharp.Portable-Singleton |
Beschreibung | Eine generische, tragbare, dokumentierte und einfach zu verwendende Singleton -Muster -Implementierung, um einzelne Instanzen durchzusetzen und zu verwalten |
Dokumentation | Vollständige Referenz v2.0.0.4 |
gesetzt |
|
Vollversion | Nuget | Bauen | Nuget Install |
---|---|---|---|
CSHARP.Portable-Singleton | PM> Install-Package CSharp.Portable-Singleton |
Sozial:
Bitte besuchen Sie hier eine vollständige Referenz, die auch im Nuget -Paket enthalten ist.
PM> Install-Package CSharp.Portable-Singleton
.using Core.Singleton;
.MySingleton : Singleton<MySingleton>
.Suchen Sie unten ein Beispiel, um einen Blick darauf zu geben, wie der Code in der Praxis aussehen wird:
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 erzwingt nicht besonders Software -Designmuster. The singleton pattern is of notable use in software as a creational design pattern , wherein only one instance of an object may be instantiated, thus generally extending the usefulness of singletons to the creation or wrapping of single-access resources.
Creating a new singleton is straightforward: Declaring an inheritance of the intended singleton class to the generic singleton class Singleton<>
suffices.
Wie zum Beispiel:
internal class MyClass : Singleton < MyClass > {
.. .
}
Ein Nutzungsbeispiel für Singletons wäre eine verbesserte Konsolenverpackung für .NET -Konsolenanwendungen. Andere typische Szenarien wären so, wo Leistung und Synchronisierungsaspekte zur Bärung gebracht werden.
Note: Arguably large scale applications running on modern platforms can resort to improved solutions over singletons particularly through framework support of design patterns.
Um loszulegen, wird empfohlen, sich an die folgende Syntax zu halten:
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 ) ;
}
There are several other ways to initialize a new Singleton<T>
instance, wherein T
is the type of the respective logical singleton class, refering to the class implementing the custom logic.
Singleton<T>.CurrentInstance
or Singleton<T>.Instance
for the first timenew T()
SingletonAttribute
such as [Singleton]class T : Singleton<T>{...}
and subsequently calling Initialize()
from a Singletonmanager
instanceActivator.CreateInstance(typeof(T));
new T(...)
SingletonManager
(see below)TypeInfo
Extension Method ToSingleton()
eg typeof(MyClass).GetTypeInfo().ToSingleton()
Examples
for code and case scenarios The generic Singleton<T>
construct has the following static properties, which are referenced in 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 ,
In besonderen Fällen ist die Entsorgung hilfreich oder sogar notwendig. See the Examples for cases.
myobj is ISingleton
typeof(MyClass).GetTypeInfo().IsSingleton()
Respectively, omit the call to GetTypeInfo()
as shown above, if the comparison type is already a TypeInfo
instance.
(Instance == null)
The following properties follow the convention of INotifyPropertyChanged
but do not implement it, whilst using a custom typed SingletonPropertyEventHandler
instead of the cannonical PropertyChangedEventHandler
.
The event PropertyChanged
itself is declared static to allow listening to Disposed
and Initialized
even when the singleton instance itself is disposed and free for garbage collection.
public static event SingletonEventHandler PropertyChanged ;
Additionally, an event is triggered when the property Manager
changes. This property is used for setter dependency injection of a SingletonManager instance implementing ISingletonManager
.
In case of several singleton classes in a given project, it is recommended to use and pass around a SingletonManager
instance.
For instance to listen to the Disposed
event for post-cleanup tasks, during the shutdown or exiting of an application, one may use a similar code-sample as follows:
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 ( ) ;
Note, that the singleton does not even have to be initialized at this point, making it safe to intialize typical IStream
elements within the singleton constructor.
The EventHandler of PropertyChanged
passes an instance of ISingleton
as the first argument, and as second parameter an instance of SingletonPropertyEventArgs
, which contains the following properties:
Name
: a string containing the name of the changed propertyValue
: the boxed current value of the propertyProperty
: the property encoded as an enum value of SingletonProperty
The following code excerpt creates a new SingletonPropertyEventArgs
instance:
var propertyName = SingletonProperty . Instance . ToString ( ) ;
var propertyValue = 100 ;
var args = new SingletonPropertyEventArgs ( SingletonProperty . Initialized , propertyValue ) ;
The following example demonstrates the dynamic use of GetValue
within an EventHandler, to access singleton properties not known until runtime.
Singelton < MyClass > . PropertyChanged += ( sender , arg ) =>
{
if ( arg . Property == SingletonProperty . Initialized )
{
var value = sender . GetValue ( " Value " ) ;
}
} ;
Generally, it is recommended to accss properties of similar singletons through custom interfaces (ie ISingletonTemplate<TCommonDenominator>
) and perform specific typechecks using the is
operator alongside explicit casts:
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
}
} ;
In the following example the class AClass
implements the 'singleton business logic', and inherits from Singleton<>
.
It suffices to include the assemblies, namespaces and derivation : Singleton<AClass>
to get the expected, tested behavior:
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 ) ;
}
Note: Many more examples are provided in full, within the examples folder.
Dieses obige Beispiel liefert das erwartete Ergebnis von:
Running: Examples.Example1. Press any key to quit...
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
A Singleton class can throw a SingletonException
(See Fig 1).
These are referenced in 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 ,
For global initialization as well as constriction the purpose of a singleton, the logical Singleton class should always be attributed with [Singleton]
as shown in the following code example:
[ Singleton ( disposable : false , initByAttribute : false , createInternal : true ) ]
public class AClass : Singleton < AClas > {
.. .
}
Das Attribut enthält drei zugängliche Eigenschaften:
Disposable
(default=false): Set to true
if the is allowed to be disposedCreateInternal
(default=true): Set to false
if the Singleton is only supposed to be instantiated externally by explicit declaration within the user source-codeInitByAttribute
(default=true): Set to true
to allow joint initialization by the SingletonManager
method Initialize
To manage several singleton types and instances throughout a large application, use the SingletonManager
class as follows:
The following example iterates over a Pool
of Singletons and performs logic dependent on the type of 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);
}
}
The singletonManager.Pool
property provides access to a thread-safe, ConcurrentDictionary<Type, ISingleton>
instance which allows for writing queries in familiar LINQ Syntax.
Disposed Singletons are never deleted but are set to null
using the SingletonManager's AddOrUpdate
method.
Um neue Instanzen eines bekannten Typs zu erstellen, verwenden Sie die generische Erstellung wie folgt:
var singletonManager = new SingletonManager ( ) ;
var gameStatics = singletonManager . CreateSingleton < GameStatics > ( ) ;
Wenn der Typ nur zur Laufzeit bekannt ist oder den Typ dynamisch als Argument übergeben wird, wie im folgenden Code -Beispiel gezeigt:
var singletonManager = new SingletonManager ( ) ;
var getInstance = ( type ) => {
var gameStatics = singletonManager . CreateSingleton ( type ) ;
} ;
getInstance ( typeof ( GameStatics ) ) ;
Es gibt nichts in der Singleton -Klasse selbst, das die Implementierung eines Serialisierers verhindern würde, aber die Implementierung und Tests liegt in den Händen des Entwicklers.
Generische Lösungen werden nicht empfohlen, sondern bei Bedarf spezifische, getestete Implementierungen dieser Singletons. Zum Beispiel in einem Szenario zum Hibernate / Lebenslauf. Es wird empfohlen, den SingletonManager für diesen Zweck zu verwenden.
Schauen Sie sich diese Diskussion auch an
Diese Bibliothek wurde vom Xunit -Test -Framework getestet. Tests are run with several classes, one of with AClass
adhering to a straightforward cannonical inheritance schema:
Abb. 1:
Wenn Sie sicher sein, dass Sie einen Fehler gestoßen haben, geben Sie bitte ein neues Problem hier.
In einem verschachtelten Erbszenario mehrerer Klassen, die hierarchisch hierarchisch erben, und eine entschlossene Basisklasse, die von Singleton stammt, ist es wichtig, die logische Singleton -Klasse zu definieren. Dies ist die Klasse, die die Logik des Singleotn nach der einzelnen Verantwortung implementieren soll.
It it also the class that determines the generic type T
from which the base class - the class short of Singleton<T>
itself, must inherit Singleton<T>
For a more complex inheritance singleton scenario, please refer to README_Example_Advanced.md
Um eine gute Lesbarkeit bei der Verwendung dieser Bibliothek beizubehalten:
singleton<T>
: eg ParentOfParentOfAClass.Instance
is OK, but avoid AClass.Instance
SingletonAttribute