| ผู้เขียน | |
|---|---|
| เว็บไซต์ | https://github.com/lsauer/csharp-singleton |
| ใบอนุญาต | ใบอนุญาต MIT |
| ปัจจุบัน | |
| บรรจุุภัณฑ์ | PM> Install-Package CSharp.Portable-Singleton |
| คำอธิบาย | การใช้งานรูปแบบ Singleton ทั่วไปแบบพกพาได้รับการบันทึกและใช้งานง่ายเพื่อบังคับใช้และจัดการอินสแตนซ์เดียว |
| เอกสาร | การอ้างอิงที่สมบูรณ์ v2.0.0.4 |
| ซบเซา |
|
| เวอร์ชันเต็ม | นูเกต | สร้าง | การติดตั้ง NuGet |
|---|---|---|---|
| csharp.portable-singleton | PM> Install-Package CSharp.Portable-Singleton |
ทางสังคม:
กรุณาเยี่ยมชมที่นี่เพื่อการอ้างอิงที่สมบูรณ์ซึ่งรวมอยู่ในแพ็คเกจ NUGET
PM> Install-Package CSharp.Portable-Singletonusing Core.Singleton; -MySingleton : Singleton<MySingleton>ค้นหาตัวอย่างด้านล่างเพื่อให้เห็นว่ารหัสจะเป็นอย่างไรในทางปฏิบัติ:
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 ไม่ได้บังคับใช้รูปแบบการออกแบบซอฟต์แวร์โดยเฉพาะ รูปแบบ Singleton นั้นมีการใช้งานซอฟต์แวร์ที่โดดเด่นเป็น รูปแบบการออกแบบที่สร้างสรรค์ ซึ่งมีเพียงอินสแตนซ์เดียวของวัตถุที่อาจมีการสร้างอินสแตนซ์ดังนั้นโดยทั่วไปแล้วจะขยายประโยชน์ของ Singletons ไปสู่การสร้างหรือห่อทรัพยากรการเข้าถึงเดี่ยว
การสร้างซิงเกิลใหม่นั้นตรงไปตรงมา: การประกาศมรดกของคลาสซิงเกิลที่ตั้งใจไว้ในคลาส Singleton ทั่วไป Singleton<> พอเพียง
เช่น:
internal class MyClass : Singleton < MyClass > {
.. .
}ตัวอย่างการใช้งานสำหรับ Singletons จะเป็น wrapper คอนโซลที่ดีขึ้นสำหรับแอปพลิเคชันคอนโซล. NET สถานการณ์ทั่วไปอื่น ๆ จะเป็นเช่นนั้นในกรณีที่ประสิทธิภาพและการซิงโครไนซ์ถูกนำมาใช้
หมายเหตุ: แอพพลิเคชั่นขนาดใหญ่ขนาดใหญ่ที่ทำงานบนแพลตฟอร์มที่ทันสมัยสามารถหันไปใช้โซลูชั่นที่ดีขึ้นเหนือ Singletons โดยเฉพาะอย่างยิ่งผ่านการสนับสนุนกรอบการออกแบบรูปแบบการออกแบบ
ในการเริ่มต้นขอแนะนำให้ปฏิบัติตามไวยากรณ์ต่อไปนี้:
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 ) ;
} มีหลายวิธีในการเริ่มต้นอินสแตนซ์ Singleton<T> ใหม่ T เป็นประเภทของคลาส Singleton แบบลอจิคัล ที่เกี่ยวข้องโดยอ้างอิงจากคลาสที่ใช้ตรรกะที่กำหนดเอง
Singleton<T>.CurrentInstance หรือ Singleton<T>.Instance เป็นครั้งแรกnew T()SingletonAttribute เช่น [Singleton]class T : Singleton<T>{...} และต่อมาเรียก Initialize() จากอิน SingletonmanagerActivator.CreateInstance(typeof(T));new T(...)SingletonManager (ดูด้านล่าง)TypeInfo ToSingleton() เช่น typeof(MyClass).GetTypeInfo().ToSingleton()Examples สำหรับรหัสและสถานการณ์กรณี โครงสร้างทั่วไป Singleton<T> มีคุณสมบัติคงที่ดังต่อไปนี้ซึ่งอ้างอิงใน 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 ,ในกรณีพิเศษการกำจัดมีประโยชน์หรือจำเป็น ดู ตัวอย่าง สำหรับกรณี
myobj is ISingletontypeof(MyClass).GetTypeInfo().IsSingleton() ตามลำดับให้ละเว้นการโทรไปยัง GetTypeInfo() ดังที่แสดงด้านบนหากประเภทการเปรียบเทียบเป็นอินสแตนซ์ TypeInfo อยู่แล้ว
(Instance == null) คุณสมบัติต่อไปนี้เป็นไปตามอนุสัญญาของ INotifyPropertyChanged แต่ไม่ได้ใช้งานในขณะที่ใช้ SingletonPropertyEventHandler ที่พิมพ์เองแทนที่จะเป็น PropertyChangedEventHandler
Event PropertyChanged นั้นมีการประกาศแบบคงที่เพื่อให้การฟัง Disposed และ Initialized แม้ว่าจะมีการกำจัดอินสแตนซ์ซิงเกิลและฟรีสำหรับการรวบรวมขยะ
public static event SingletonEventHandler PropertyChanged ; นอกจากนี้เหตุการณ์จะถูกทริกเกอร์เมื่อ Manager ทรัพย์สินเปลี่ยนแปลง สถานที่ให้บริการนี้ใช้สำหรับการฉีดพึ่งพาผู้ตั้งค่าของอินสแตนซ์ SingletonManager ที่ใช้ ISingletonManager
ในกรณีที่มีชั้นเรียนซิงเกิลหลายชั้นในโครงการที่กำหนดขอแนะนำให้ใช้และส่งผ่านอินสแตนซ์ SingletonManager
ตัวอย่างเช่นในการฟังเหตุการณ์ Disposed สำหรับงานโพสต์ทำความสะอาดในระหว่างการปิดเครื่องหรือออกจากแอปพลิเคชันหนึ่งอาจใช้ตัวอย่างโค้ดที่คล้ายกันดังนี้:
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 ( ) ; โปรดทราบว่าซิงเกิลตันไม่จำเป็นต้องเริ่มต้น ณ จุดนี้ทำให้ปลอดภัยที่จะทำให้องค์ประกอบ IStream ทั่วไปภายในตัวสร้างซิงเกิลตัน
EventHandler ของ PropertyChanged ผ่านอินสแตนซ์ของ ISingleton เป็นอาร์กิวเมนต์แรกและเป็นพารามิเตอร์ที่สองอินสแตนซ์ของ SingletonPropertyEventArgs ซึ่งมีคุณสมบัติต่อไปนี้:
Name : สตริงที่มีชื่อของคุณสมบัติที่เปลี่ยนแปลงValue : มูลค่าปัจจุบันที่กล่องของคุณสมบัติProperty : คุณสมบัติที่เข้ารหัสเป็นค่า enum ของ SingletonProperty ข้อความที่ตัดตอนมาจากรหัสต่อไปนี้สร้างอินสแตนซ์ SingletonPropertyEventArgs ใหม่:
var propertyName = SingletonProperty . Instance . ToString ( ) ;
var propertyValue = 100 ;
var args = new SingletonPropertyEventArgs ( SingletonProperty . Initialized , propertyValue ) ; ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการใช้งานแบบไดนามิกของ GetValue ภายใน eventhandler เพื่อเข้าถึงคุณสมบัติ Singleton ที่ไม่รู้จักจนถึงรันไทม์
Singelton < MyClass > . PropertyChanged += ( sender , arg ) =>
{
if ( arg . Property == SingletonProperty . Initialized )
{
var value = sender . GetValue ( " Value " ) ;
}
} ; โดยทั่วไปขอแนะนำให้ใช้คุณสมบัติ ACCSS ของ Singletons ที่คล้ายกันผ่านอินเตอร์เฟสที่กำหนดเอง (เช่น ISingletonTemplate<TCommonDenominator> ) และดำเนินการ typeChecks เฉพาะโดยใช้ตัวดำเนินการ is พร้อมกับ 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
}
} ; ในตัวอย่างต่อไปนี้คลาส AClass ใช้ 'Singleton Business Logic' และสืบทอดมาจาก Singleton<>
มันพอเพียงที่จะรวมแอสเซมบลีเนมสเปซและการสืบทอด : Singleton<AClass> เพื่อให้ได้พฤติกรรมที่คาดหวัง:
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 ) ;
}หมายเหตุ: มี ตัวอย่าง เพิ่มเติมมากมายภายในโฟลเดอร์ตัวอย่าง
ตัวอย่างข้างต้นนี้จะให้ผลลัพธ์ที่คาดหวังของ:
Running: Examples.Example1. Press any key to quit...
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main '
Expected: ' Main ' ; Observed: ' Main ' คลาส Singleton สามารถโยน SingletonException (ดูรูปที่ 1)
สิ่งเหล่านี้มีการอ้างอิงใน 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 , สำหรับการเริ่มต้นทั่วโลกเช่นเดียวกับการหดตัววัตถุประสงค์ของซิงเกิลตันคลาส Singleton แบบลอจิคัลควรนำมาประกอบกับ [Singleton] ตามที่แสดงในตัวอย่างรหัสต่อไปนี้:
[ Singleton ( disposable : false , initByAttribute : false , createInternal : true ) ]
public class AClass : Singleton < AClas > {
.. .
}แอตทริบิวต์มีคุณสมบัติที่เข้าถึงได้สามประการ:
Disposable (default = false): ตั้งค่าเป็น true หากได้รับอนุญาตให้จำหน่ายCreateInternal (default = true): ตั้งค่าเป็น false หากซิงเกิลตันควรจะเป็นอินสแตนซ์ภายนอกโดยการประกาศอย่างชัดเจนภายในรหัสแหล่งที่มาของผู้ใช้InitByAttribute (default = true): ตั้งค่าเป็น true เพื่ออนุญาตให้เริ่มต้นร่วมกันโดยวิธี Initialize SingletonManager ในการจัดการประเภทและอินสแตนซ์หลายประเภทตลอดแอปพลิเคชันขนาดใหญ่ให้ใช้คลาส SingletonManager ดังนี้:
ตัวอย่างต่อไปนี้วนซ้ำผ่าน Pool ของซิงเกิลและดำเนินการตรรกะขึ้นอยู่กับประเภทของซิงเกิลตัน:
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);
}
}
คุณสมบัติ singletonManager.Pool ให้การเข้าถึงอินสแตนซ์ของเธรดที่ปลอดภัย ConcurrentDictionary<Type, ISingleton> อินสแตนซ์ซึ่งอนุญาตให้เขียนแบบสอบถามในไวยากรณ์ LINQ ที่คุ้นเคย
Singletons ที่ถูกกำจัดจะไม่ถูกลบ แต่ถูกตั้งค่าเป็น null โดยใช้วิธี AddOrUpdate ของ SingletonManager
ในการสร้างอินสแตนซ์ใหม่ของประเภทที่รู้จักให้ใช้วิธีการ createInstance ทั่วไปดังนี้:
var singletonManager = new SingletonManager ( ) ;
var gameStatics = singletonManager . CreateSingleton < GameStatics > ( ) ;หากประเภทนั้นเป็นที่รู้จักในรันไทม์หรือใช้งานได้แบบไดนามิกเป็นอาร์กิวเมนต์ตามที่แสดงในตัวอย่างรหัสต่อไปนี้:
var singletonManager = new SingletonManager ( ) ;
var getInstance = ( type ) => {
var gameStatics = singletonManager . CreateSingleton ( type ) ;
} ;
getInstance ( typeof ( GameStatics ) ) ;ไม่มีอะไรในคลาส Singleton ที่จะป้องกันการใช้งาน serializer อย่างไรก็ตามการดำเนินการเช่นเดียวกับการทดสอบอยู่ในมือของนักพัฒนา
ไม่แนะนำให้ใช้โซลูชันทั่วไป แต่มีการทดสอบที่เฉพาะเจาะจงและผ่านการทดสอบของซิงเกิลเหล่านั้นในกรณีที่จำเป็น ตัวอย่างเช่นในสถานการณ์รัฐฮิมเบอร์ / กลับมาทำงานต่อ ขอแนะนำให้ใช้ขยาย SingletonManager เพื่อจุดประสงค์นั้น
ลองดูที่การสนทนานี้
ห้องสมุดนี้ได้รับการทดสอบโดยกรอบการทดสอบ XUNIT การทดสอบดำเนินการกับหลายชั้นเรียนซึ่งเป็นหนึ่งใน AClass ที่ยึดติดกับสคีมาการสืบทอดทางปืนถักแบบตรงไปตรงมา:
รูปที่ 1:
หากคุณแน่ใจว่าคุณพบข้อผิดพลาดโปรดผลักดันปัญหาใหม่ที่นี่
ในสถานการณ์การสืบทอดที่ซ้อนกันของหลายชั้นเรียนที่สืบทอดซึ่งกันและกันตามลำดับชั้นและคลาสฐานที่กำหนดที่ได้มาจากซิงเกิลมันเป็นสิ่งสำคัญที่จะกำหนดคลาส Singleton แบบตรรกะ นี่คือคลาสที่ตั้งใจจะใช้ตรรกะของ singleotn ตามความรับผิดชอบเดียว
มันยังเป็นคลาสที่กำหนด T ทั่วไปที่คลาสพื้นฐาน - คลาสสั้นของ Singleton<T> ตัวเองต้องสืบทอด Singleton<T>
สำหรับสถานการณ์การสืบทอดที่ซับซ้อนยิ่งขึ้นโปรดดูที่ README_Example_Advanced.md
เพื่อรักษาความสามารถในการอ่านที่ดีเมื่อใช้ห้องสมุดนี้:
singleton<T> : เช่น ParentOfParentOfAClass.Instance ก็โอเค แต่หลีกเลี่ยง AClass.InstanceSingletonAttribute