مؤلف | |
---|---|
موقع إلكتروني | https://github.com/lsauer/csharp-singleton |
رخصة | رخصة معهد ماساتشوستس للتكنولوجيا |
حاضِر | |
طَرد | PM> Install-Package CSharp.Portable-Singleton |
وصف | تنفيذ أنماط المفرد العامة ، المحمولة ، الموثقة وسهلة الاستخدام ، لتطبيق وإدارة مثيلات واحدة |
الوثائق | مرجع كامل v2.0.0.4 |
ممتلئ |
|
النسخة الكاملة | nuget | يبني | تثبيت Nuget |
---|---|---|---|
Csharp. Portable-Singleton | PM> Install-Package CSharp.Portable-Singleton |
اجتماعي:
يرجى زيارة هنا للحصول على مرجع كامل ، والذي يتم تضمينه أيضًا في حزمة Nuget.
PM> Install-Package CSharp.Portable-Singleton
.using 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 هو استخدام ملحوظ في البرامج كنمط تصميم إبداعي ، حيث يمكن إنشاء مثيل واحد فقط من كائن ، وبالتالي تمديد فائدة المفردات إلى إنشاء أو لف موارد الوصول الواحد.
إن إنشاء Singleton جديد واضح ومباشر: إعلان ميراث من فئة Singleton المقصودة إلى Singleton<>
يكفي.
مثل:
internal class MyClass : Singleton < MyClass > {
.. .
}
سيكون مثال الاستخدام للفرد على غلاف وحدة تحكم محسّن لتطبيقات وحدة التحكم .NET ، وسيكون السيناريوهات النموذجية الأخرى حيث يتم تقديم جوانب الأداء والمزامنة.
ملاحظة: يمكن القول إن التطبيقات الكبيرة التي تعمل على المنصات الحديثة يمكن أن تلجأ إلى حلول محسنة على المفردات خاصة من خلال الدعم الإطاري لأنماط التصميم.
للبدء ، يوصى بالالتزام بتجنيد الجملة التالي:
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()
من مثيل Singletonmanager
Activator.CreateInstance(typeof(T));
new T(...)
SingletonManager
(انظر أدناه)TypeInfo
ToSingleton()
eg 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 ISingleton
typeof(MyClass).GetTypeInfo().IsSingleton()
على التوالي ، حذف المكالمة إلى GetTypeInfo()
كما هو موضح أعلاه ، إذا كان نوع المقارنة هو بالفعل مثيل TypeInfo
.
(Instance == null)
تتبع الخصائص التالية اتفاقية INotifyPropertyChanged
ولكنها لا تنفذها ، مع استخدام SingletonPropertyEventHandler
المخصصة بدلاً من PropertyChangedEventHandler
المدفعية.
تم الإعلان PropertyChanged
نفسها ثابتة للسماح بالاستماع إلى Disposed
Initialized
حتى عندما يتم التخلص من مثيل Singleton نفسه ومجاني لجمع القمامة.
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 of PropertyChanged
مثيلًا لـ ISingleton
كوسيطة أول ، وكمعلمة ثانية مثيل لـ SingletonPropertyEventArgs
، التي تحتوي على الخصائص التالية:
Name
: سلسلة تحتوي على اسم الخاصية التي تم تغييرهاValue
: القيمة الحالية المعبأة للخاصيةProperty
: العقار المشفر كقيمة تعداد من 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 من المفردات المماثلة من خلال واجهات مخصصة (أي ISingletonTemplate<TCommonDenominator>
) وأداء typechces محددة باستخدام المشغل is
إلى جانب الممثلين الصريحين:
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" ، ويرث من 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]
كما هو موضح في مثال الكود التالي:
[ Singleton ( disposable : false , initByAttribute : false , createInternal : true ) ]
public class AClass : Singleton < AClas > {
.. .
}
السمة لها ثلاث خصائص يمكن الوصول إليها:
Disposable
(افتراضي = خطأ): ضبط على 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 المألوفة.
لم يتم حذف المفردات المتصاعدة أبدًا ولكن يتم تعيينها على 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 نفسها التي من شأنها أن تمنع تنفيذ المسلسل ، ولكن التنفيذ والاختبار هو في أيدي المطور.
لا ينصح بالحلول العامة ولكن تطبيقات محددة إلى حد ما لتلك المفردات عند الضرورة. على سبيل المثال في سيناريو الدولة / السيرة الذاتية. يوصى باستخدام تمديد المفرد لهذا الغرض.
ألق نظرة أيضًا على هذه المناقشة
تم اختبار هذه المكتبة من خلال إطار اختبار Xunit. يتم إجراء الاختبارات مع عدة فئات ، واحدة من AClass
تلتزم بمخطط الميراث المدني المباشر:
الشكل 1:
إذا كنت على يقين من أنك واجهت خطأ ، يرجى الضغط على مشكلة جديدة هنا.
في سيناريو الميراث المتداخل لعدة فصول ترث بعضها البعض بشكل هرمي ، وفئة قاعدة محددة مستمدة من Singleton ، من المهم تحديد فئة Singleton المنطقية. هذا هو الفئة التي تهدف إلى تنفيذ منطق singleotn بعد الولادة المسؤولية الفردية.
كما أنه الفئة التي تحدد النوع T العام T
الذي يجب أن يرث منه الفئة الأساسية - الفئة القليلة من Singleton<T>
نفسها ، Singleton<T>
لسيناريو المفرد الأكثر تعقيدًا ، يرجى الرجوع إلى README_Example_Advanced.md
للحفاظ على قابلية القراءة الجيدة عند استخدام هذه المكتبة:
singleton<T>
: على سبيل المثال ParentOfParentOfAClass.Instance
على ما يرام ، ولكن تجنب AClass.Instance
SingletonAttribute