تهيئة كسول في C #

التهيئة الكسولة ، أو التهيئة "الكسولة" ، هي طريقة للوصول إلى كائن ، حيث تختبئ خلف آلية تسمح لك بتأجيل إنشاء هذا الكائن حتى لحظة الوصول إليه لأول مرة. يمكن أن تنشأ الحاجة إلى التهيئة البطيئة لأسباب مختلفة: من الرغبة في تقليل الحمل عند بدء تشغيل التطبيق إلى تحسين الوظائف التي نادرًا ما تستخدم. في الواقع ، لا يتم استخدام جميع وظائف التطبيق دائمًا ، علاوة على ذلك ، على الفور ، لذلك فمن المنطقي تمامًا تأجيل إنشاء الكائنات التي تنفذها حتى أوقات أفضل. أرغب في مراجعة خيارات التهيئة البطيئة المتوفرة بلغة C #.



لتوضيح الأمثلة ، سأستخدم فئة الاختبار ، التي تحتوي على خاصية BlobData التي تُرجع كائنًا من النوع Blob ، والذي يتم إنشاؤه ، وفقًا لمفهوم الأسطورة ، ببطء شديد ، وقد تقرر إنشاؤه بتكاسل.



class Test
{
    public Blob BlobData
    {
        get
        {
            return new Blob();
        }
    }
}




التحقق من وجود فارغة



أبسط خيار متاح من الإصدارات الأولى للغة ، هو إنشاء متغير غير مهيأ واختباره بحثًا عن قيمة خالية قبل العودة. إذا كان المتغير فارغًا ، فأنشئ كائنًا وقم بتعيينه لهذا المتغير ، ثم أعده. عند الوصول المتكرر ، سيتم إنشاء الكائن بالفعل وسنعيده على الفور.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            if (_blob == null)
            {
                _blob = new Blob();
            }

            return _blob;
        }
    }
}




يتم هنا إنشاء كائن من نوع Blob عند الوصول إلى الخاصية لأول مرة. أو لا يتم إنشاؤه إذا لم يكن البرنامج بحاجة إليه لسبب ما في هذه الجلسة.



عامل ثلاثي ؟:



لدى C # عامل تشغيل ثلاثي يسمح لك باختبار شرط ما ، وإذا كان صحيحًا ، فقم بإرجاع قيمة واحدة ، وإذا كانت خاطئة ، فأخرى. يمكننا استخدامه لاختصار الكود وتبسيطه قليلاً.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            return _blob == null
                ? _blob = new Blob()
                : _blob;
        }
    }
}




يبقى الجوهر كما هو. إذا لم تتم تهيئة الكائن ، فقم بالتهيئة والعودة. إذا تمت تهيئته بالفعل ، فسنقوم بإعادته على الفور.



باطل



تختلف المواقف ، وقد نواجه ، على سبيل المثال ، حالة تحتوي فيها فئة Blob على عامل تشغيل زائد التحميل ==. للقيام بذلك ، ربما نحتاج إلى إجراء فحص فارغ بدلاً من == null. متوفر بأحدث إصدارات اللغة.



return _blob is null
    ? _blob = new Blob()
    : _blob;




لكن هذا هو كذلك ، استطرادية صغيرة.



عامل الاندماج الصفري ؟؟



عامل التشغيل الثنائي ؟؟ سيساعدنا في تبسيط الكود أكثر.

جوهر عمله على النحو التالي. إذا لم يكن المعامل الأول فارغًا ، فسيتم إرجاعه. إذا كان المعامل الأول فارغًا ، يتم إرجاع الثاني.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            return _blob ?? (_blob = new Blob());
        }
    }
}




يجب وضع المعامل الثاني بين قوسين بسبب أولوية العمليات.



عامل التشغيل ؟؟ =



يقدم C # 8 عامل التوحيد الصفري الذي يشبه هذا؟ =

كيف يعمل كما يلي. إذا لم يكن المعامل الأول فارغًا ، فسيتم إرجاعه ببساطة. إذا كان المعامل الأول فارغًا ، فسيتم تعيين قيمة الثاني له ويتم إرجاع هذه القيمة.



class Test
{
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            return _blob ??= new Blob();
        }
    }
}




هذا سمح لنا بتقليل الكود أكثر قليلاً.



تيارات



إذا كان هناك احتمال أن العديد من سلاسل الرسائل يمكنها الوصول إلى مورد معين في وقت واحد ، فيجب أن نجعله آمنًا. خلاف ذلك ، قد يحدث موقف ، على سبيل المثال ، سيقوم كلا الخيطين بفحص الكائن بحثًا عن فارغ ، وستكون النتيجة خاطئة ، ثم سيتم إنشاء كائنين Blob ، وتحميل النظام مرتين قدر ما أردناه ، بالإضافة إلى أحد هذه العناصر سيتم حفظ الكائنات ، وسيتم فقد الثانية.



class Test
{
    private readonly object _lock = new object();
    private Blob _blob = null;

    public Blob BlobData
    {
        get
        {
            lock (_lock)
            {
                return _blob ?? (_blob = new Blob());
            }
        }
    }
}




يكتسب بيان القفل قفلًا خاصًا بشكل متبادل على الكائن المحدد قبل تنفيذ عبارات معينة ، ثم يحرر القفل. وهو يعادل استخدام System.Threading.Monitor.Enter (...، ...) ؛



كسول <T>



قدم .NET 4.0 فئة Lazy لإخفاء كل هذا العمل القذر عن أعيننا. الآن يمكننا فقط ترك متغير محلي من النوع Lazy. عند الوصول إلى خاصية Value الخاصة بها ، نحصل على كائن من فئة Blob. إذا تم إنشاء الكائن في وقت سابق ، فسيعود على الفور ، وإلا فسيتم إنشاؤه أولاً.



class Test
{
    private readonly Lazy<Blob> _lazy = new Lazy<Blob>();

    public Blob BlobData
    {
        get
        {
            return _lazy.Value;
        }
    }
}




نظرًا لأن فئة Blob بها مُنشئ بدون معلمات ، يمكن لـ Lazy إنشاؤها في الوقت المناسب دون أي أسئلة. إذا احتجنا إلى تنفيذ بعض الإجراءات الإضافية أثناء إنشاء كائن Blob ، فيمكن لمنشئ الفئة Lazy الرجوع إلى Func <T>



private Lazy<Blob> _lazy = new Lazy<Blob>(() => new Blob());




بالإضافة إلى ذلك ، في المعلمة الثانية للمنشئ ، يمكننا تحديد ما إذا كنا بحاجة إلى أمان الخيط (نفس القفل).



خاصية



الآن دعنا نخفض خاصية القراءة فقط ، لأن C # الحديث يتيح لك القيام بذلك بشكل جيد. في النهاية ، كل شيء يبدو كما يلي:



class Test
{
    private readonly Lazy<Blob> _lazy = new Lazy<Blob>();
    public Blob BlobData => _lazy.Value;
}




LazyInitializer



يوجد أيضًا خيار عدم التفاف الفصل في غلاف Lazy ، ولكن بدلاً من ذلك استخدم وظيفة LazyInitializer. تحتوي هذه الفئة على طريقة ثابتة واحدة تأكد من التهيئة مع مجموعة من الأحمال الزائدة التي تسمح لك بإنشاء أي شيء ، بما في ذلك أمان الخيط وكتابة رمز مخصص لإنشاء كائن ، ولكن جوهرها الرئيسي هو كما يلي تحقق مما إذا كان الكائن غير مهيأ. إذا لم يكن كذلك ، فقم بالتهيئة. إرجاع كائن. باستخدام هذا الفصل ، يمكننا إعادة كتابة الكود الخاص بنا على النحو التالي:



class Test
{
    private Blob _blob;
    public Blob BlobData => LazyInitializer.EnsureInitialized(ref _blob);
}




هذا كل شئ. شكرآ لك على أهتمامك.



All Articles