أهم أقسام المدونة

الصفحات

الأحد، 17 مارس 2019

معامل معالجة العدم Null-Coalesce Operator

معامل معالجة العدم Null-Coalesce Operator:
تمتلك كل من فيجوال بيزيك وسي شارب معامل معالجة العدم Null-Coalesce Operator، وهو يستقبل متغيرين، حيث يعيد قيمة المتغير الأول لو كانت لا تساوي null (أو Nothing في فيجوال بيزيك)، وإلا فإنه يعيد قيمة المتغير الثاني.
وتمتاز هذه الصيغة في سي شارب بأنها مختصرة وتسمح بالاختيار من سلسلة طويلة من البدائل ببساطة:
var r = a ?? b ?? c ?? d ?? e;
العبارة (a ?? b ?? c ?? d ?? e) ستعيد قيمة أول متغير غير منعدم (قيمته لا تساوي null)، فإذا كانت كل المتغيرات منعدمة، فستعيد null.
للأسف فعل نفس الشيء في فيجوال بيزيك ليس بنفس البساطة، لأنها تستخدم if() لفحص قيمة متغيرين فقط، ولو أردت تنفيذ الكود السابق فستحتاج إلى استخدام عدة جمل If متداخلة كالتالي:
Dim r = If(a, If(b, If(c, If(d, e))))
قد لا يبدو لك الأمر هاما، لكن واقعيا، a, b , c, d, f تكون عبارات برمجية طويلة وليست مجرد أسماء مختصرة كهذه.. مثلا:
C#:
var t = CheckToolStrip(c as ToolStrip) ??
            CheckToolStrip(c.ContextMenuStrip) ??
            CheckControls(c);
 
VB.NET:
Dim t = If(CheckToolStrip(TryCast(c, ToolStrip)),
                  If(CheckToolStrip(c.ContextMenuStrip),
                       CheckControls(c)))
لو حاولت أن تضيف قيمة رابعة للكود السابق في سي شارب، فيكفي أن تضيف العلامة ؟؟ في السطر الأخير ثم تكتب العبارة الجديدة.. أما في فيجوال بيزيك، فعليك أن تضيف جملة If جديدة في البداية، وتجعل معاملها الأول هو العبارة الجديد، ومعاملها الثاني هو كل الكود السابق!
لحل هذه المشكلة، كتبت هذه الدالة:
Function Coalesce(Of T)(ParamArray expressions() As T) As T
        For Each expr In expressions
            If expr IsNot Nothing Then Return expr
        Next
End Function
وبهذا يمكن استخدامها كالتالي:
dim x = Coalesce(a, b, c, d, e)
هذا الكود أوضح وأسهل، لكن فيه مشكلة تجعله أقل كفاءة، وهي أن فيجوال بيزيك ستحسب قيم كل العبارات لإرسالها إلى معاملات الدالة Coalesce.. هذه العبارات قد تكون دوالا كما في هذا المثال:
Dim t = Coalesce(CheckToolStrip(TryCast(c, ToolStrip)),
              CheckToolStrip(c.ContextMenuStrip),
              CheckControls(c))
استخدام الدالة Coalesce في الكود السابق سيضمن استدعاء الدوال الثلاث حتى لو كانت الدالة الأولى فقط هي التي ستعيد قيمتها.. وفي المشروع الذي كتبت فيه هذا الكود، هذه الدوال دوال ارتدادية Recursive Functions، أي أنها تظل تستدعي نفسها إلى أن تصل إلى نهاية شجرة العناصر، وهذا معناه أن تنفيذ دالة أو دالتين بدون داعي سيستهلك وقتا ملموسا!
هذا لا يحدث عند استخدام If() لأنها تتجاهل تنفيذ أي عبارات لا لزوم لها (يسمى هذا Short-Circuiting  وهو تعبير مأخوذ من مصطلح جعل الدائرة الكهربية قصيرة، أو بالتعبير الشائع: عمل قفلة).
لحل هذه المشكلة، يمكن اللجوء إلى حيلة صغيرة، وهي ألا نرسل العبارات مباشرة، وإنما نضع كل عبارة في دالة بدون معاملات كل مهمتها أن تحسب قيمة العبارة وتعيدها.. ونطور الدالة Coalesce لنجعلها تستقبل دالة العبارات بدلا من العبارات نفسها كالتالي:
Function Coalesce(Of T)(ParamArray expressions( ) As Func(Of T)) As T
        For Each exp In expressions
            Try
                Dim v = exp( )
                If v IsNot Nothing Then Return v
            Catch
            End Try
        Next
        Return Nothing
End Function
لاحظ أنني طورت كود الدالة بحيث تعالج أي أخطاء تحدث عند استدعاء دالة حساب قيمة العبارة، وهذا معناه أن الدالة Coalesce ستتجاهل القيم المنعدمة أو التي حدث خطأ عند حسابها.
الآن، وباستخدام الدوال الفورية، صار من السهل استخدام هذه الدالة كالتالي:
Dim t = Coalesce(
              Function( ) CheckToolStrip(TryCast(c, ToolStrip)),
              Function( ) CheckToolStrip(c.ContextMenuStrip),
              Function( ) CheckControls(c))
هذا الكود سيرسل عناوين الدوال إلى الدالة Coalesce، وهي التي ستقوم باستدعاء كل دالة واحدة تلو أخرى، إلى أن تجد قيمة غير منعدمة فتعيدها وتتوقف عن استدعاء باقي الدوال.. وهذا هو الأداء المطلوب.
قد يبدو هذا الكود أطول قليلا من استخدام if ولكنه أكثر وضوحا بسبب عدم تداخل جمل if وأقواسها الكثيرة.
وقد اقترحت أن تضيف فيجوال بيزيك الجملة Coalesce لتعمل بآلية short-circuiting مباشرة بدون أن نضطر نحن إلى استخدام الدوال الفورية، وأتمنى أن يستجيبوا لهذا المقترح.
 

ليست هناك تعليقات:

إرسال تعليق

ملحوظة: يمكن لأعضاء المدونة فقط إرسال تعليق.