فرض استدعاءات الدوال المطلوبة في C#
المشكلة: استدعاءات الدوال غير المتحققة
في C#
، من الشائع جداً إنشاء دوال تُرجع حالة، مما يساعد المطورين على مراقبة نجاح أو فشل العمليات. ومع ذلك، فإنه من الشائع أن يتجاهل بعض المطورين هذه الحالات المعادة تمامًا. يمكن أن يؤدي ذلك إلى عواقب غير مقصودة إذا لم يتم تنفيذ معالجة الأخطاء بشكل صحيح.
على سبيل المثال، اعتبر وجود فصل Status
، الذي قد يحتوي على معلومات حول ما إذا كانت العملية ناجحة أو إذا كانت هناك مشاكل أثناء التنفيذ:
Status MyFunction()
{
if(...) // شيء سيء
return new Status(false, "حدث خطأ ما");
else
return new Status(true, "حسناً");
}
التوقع هو أن جميع المستدعين لـ MyFunction
يجب أن يتحققوا من الحالة المعادة:
Status myStatus = MyFunction();
if (!myStatus.IsOK())
// تعامل مع ذلك، اعرض رسالة،...
ومع ذلك، قد يتجاهل بعض المستدعين بشكل غير مبرر التحقق من الحالة:
// تجاهل الحالة المعادة
MyFunction(); // استدعاء الدالة وتجاهل الحالة المعادة
هل يمكن منع ذلك؟
هذا يثير سؤالًا مهمًا: كيف يمكننا ضمان أن كل مستدعي يتحقق من الحالة المعادة؟ في C++
، يمكن استخدام مُدمر للتحقق من الحالة في نهاية دورة حياة الكائن. ومع ذلك، في C#
، يتم النظر بشكل سلبي عمومًا إلى المُدمرات بسبب طبيعة جامع القمامة غير الحتمية.
الحل: استخدام المندوبين لاستدعاءات الدوال المطلوبة
على الرغم من أن C#
تفتقر إلى القدرة على فرض قيمة إرجاع طريقة لتُستدعى أو تُتحقق منها، فإن أسلوب مبتكر يتضمن استخدام المندوبين. هذه التقنية لا تُرجع مجرد قيمة؛ بل تتطلب من المستدعي معالجة الحالة المعادة بشكل صريح من خلال آلية رد نداء.
مثال على التنفيذ
دعنا نستعرض تنفيذًا بسيطًا باستخدام المندوبين:
using System;
public class Example
{
public class Toy
{
private bool inCupboard = false;
public void Play() { Console.WriteLine("اللعب."); }
public void PutAway() { inCupboard = true; }
public bool IsInCupboard { get { return inCupboard; } }
}
public delegate void ToyUseCallback(Toy toy);
public class Parent
{
public static void RequestToy(ToyUseCallback callback)
{
Toy toy = new Toy();
callback(toy);
if (!toy.IsInCupboard)
{
throw new Exception("لم تضع لعبتك في الخزانة!");
}
}
}
public class Child
{
public static void Play()
{
Parent.RequestToy(delegate(Toy toy)
{
toy.Play();
// عذرًا! نسيت أن أضع اللعبة بعيدًا!
});
}
}
public static void Main()
{
Child.Play();
Console.ReadLine();
}
}
شرح الكود
في هذا المثال، عندما يتم استدعاء Parent.RequestToy
، فإنه يتوقع وظيفة رد نداء ستؤدي عمليات مع كائن Toy
. بعد تنفيذ رد النداء، يتم التحقق مما إذا كانت اللعبة قد تم وضعها بعيدًا:
- إذا لم يستدعِ رد النداء
toy.PutAway()
، يتم رمي استثناء. وهذا يضمن أن المستدعي يجب أن يتعامل معToy
في نهاية استخدامها، مما يفرض بشكل فعال أن تتم العمليات اللازمة بعد ذلك. - هذه الطريقة لا تضمن أن المستدعي سيذكر التحقق من الحالة، لكنها تفرض هيكلًا بطريقة تُعلي من أهمية العملية.
الخاتمة
بينما قد لا يكون من الممكن في C#
فرض التحقق من القيم المعادة بنفس الصرامة كما هو الحال في C++
، يمكننا الاستفادة من المندوبين لإنشاء نمط يوضح بشكل فعال ضرورة المزيد من الإجراءات، مما يعزز موثوقية كودنا. من خلال استخدام هذا النمط، نضمن أن المطورين الذين يستخدمون كودك لا يمكنهم تخطي الفحوصات الأساسية بسهولة. على الرغم من أنه قد يبدو غير تقليدي بعض الشيء، إلا أنه يعد وسيلة مفيدة لإنشاء ثقافة المساءلة في معالجة الأخطاء.