Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

الـ Delegates والأحداث

جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 43 من 70.

الـ delegate هو نوع يمثل مرجعاً لدالة (method). فكر فيه كمتغير يحمل دالة بدلاً من البيانات. يتيح لك ذلك تمرير الدوال كـ arguments، وتخزينها في مجموعات، واستدعاؤها بشكل ديناميكي.

تقوم بالتصريح عن مفوض (delegate) من خلال تحديد توقيع الطريقة (method signature) التي يمكنه الرجوع إليها:

public delegate void Notify(string message);

public class Publisher
{
    public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}

// الاستخدام
Notify notifier = new Publisher().SendAlert;
notifier("System started");  // Alert: System started

تعتمد الأحداث (Events) على المفوضات (delegates) لتنفيذ نمط النشر والاشتراك (publish-subscribe pattern). تقوم الفئة (class) بإطلاق حدث، ويمكن للفئات الأخرى الاشتراك لتلقي الإشعارات. تقيد الكلمة المفتاحية event كيفية استخدام المفوض - حيث يمكن للمشتركين فقط إضافة أو إزالة المعالجات (handlers)، ولا يمكنهم استدعاء الحدث مباشرة.

public class Button
{
    public event Action Clicked;
    
    public void Click()
    {
        Clicked?.Invoke();  // إخطار جميع المشتركين
    }
}

// الاشتراك
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click();  // تم النقر على الزر!

يقوم عامل += بالاشتراك بمعالج، بينما يقوم -= بإلغاء الاشتراك. يستدعي نمط ?.Invoke() الحدث بأمان فقط في حالة وجود مشتركين. تُعد الأحداث أساسية لتطبيقات واجهة المستخدم الرسومية (GUI)، ومحركات الألعاب، وأي نظام تحتاج فيه الكائنات إلى التفاعل مع التغييرات دون ارتباط وثيق.

challenge icon

التحدي

سهل

لنقم ببناء نظام مراقبة درجة الحرارة الذي يستخدم المفوضات (delegates) والأحداث (events) لإخطار المشتركين كلما تغيرت درجة الحرارة. هذا مثال كلاسيكي على نمط النشر والاشتراك (publish-subscribe pattern) الذي تتيحه الأحداث بشكل أنيق للغاية.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • TemperatureMonitor.cs: قم بإنشاء فئة TemperatureMonitor في مساحة الأسماء Monitoring والتي تعمل كناشر. يجب أن يقوم المراقب الخاص بك بما يلي:
    • امتلاك حقل خاص (private field) لتخزين درجة الحرارة الحالية
    • الإعلان عن حدث باستخدام Action<int> يسمى TemperatureChanged ينطلق كلما تم تحديث درجة الحرارة
    • امتلاك خاصية Temperature (من نوع int) حيث يقوم الـ setter بتحديث الحقل الخاص ويرفع الحدث بقيمة درجة الحرارة الجديدة
    تذكر استخدام نمط ?.Invoke() لرفع الحدث بأمان فقط عندما يكون هناك مشتركون.
  • Program.cs: في ملفك الرئيسي، قم بإنشاء TemperatureMonitor واشترك بمعالجين مختلفين في حدث TemperatureChanged الخاص به:
    • معالج واحد يطبع: Alert: Temperature is now {value} degrees
    • معالج آخر يطبع: Logged: {value}
    بعد الاشتراك في كلا المعالجين، قم بتعيين درجة الحرارة مرتين باستخدام قيم من المدخلات. في كل مرة تقوم فيها بتعيين درجة الحرارة، يجب أن يستجيب كلا المعالجين.

ستتلقى مدخلين:

  • قيمة درجة الحرارة الأولى (عدد صحيح)
  • قيمة درجة الحرارة الثانية (عدد صحيح)

على سبيل المثال، إذا كانت المدخلات هي 25 و 30، يجب أن يكون المخرج:

Alert: Temperature is now 25 degrees
Logged: 25
Alert: Temperature is now 30 degrees
Logged: 30

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

ورقة مرجعية

الـ delegate هو نوع يمثل مرجعاً إلى طريقة (method)، مما يسمح لك بتمرير الطرق كوسطاء، وتخزينها في مجموعات، واستدعائها ديناميكياً.

قم بالتصريح عن delegate من خلال تحديد توقيع الطريقة (method signature):

public delegate void Notify(string message);

public class Publisher
{
    public void SendAlert(string msg) => Console.WriteLine("Alert: " + msg);
}

// الاستخدام
Notify notifier = new Publisher().SendAlert;
notifier("System started");  // Alert: System started

تقوم الـ Events بتنفيذ نمط النشر والاشتراك (publish-subscribe pattern) باستخدام الـ delegates. تقيد الكلمة المفتاحية event استخدام الـ delegate - حيث يمكن للمشتركين فقط إضافة أو إزالة المعالجات (handlers)، وليس استدعاء الحدث مباشرة.

public class Button
{
    public event Action Clicked;
    
    public void Click()
    {
        Clicked?.Invoke();  // إخطار جميع المشتركين
    }
}

// الاشتراك
Button btn = new Button();
btn.Clicked += () => Console.WriteLine("Button was clicked!");
btn.Click();  // Button was clicked!

استخدم += للاشتراك في معالج و -= لإلغاء الاشتراك. يقوم نمط ?.Invoke() باستدعاء الحدث بأمان فقط في حالة وجود مشتركين.

جرّب بنفسك

using System;
using Monitoring;

class Program
{
    public static void Main(string[] args)
    {
        // قراءة المدخلات
        int temp1 = Convert.ToInt32(Console.ReadLine());
        int temp2 = Convert.ToInt32(Console.ReadLine());

        // TODO: إنشاء مثيل (instance) من TemperatureMonitor

        // TODO: الاشتراك بمعالج (handler) يطبع: "Alert: Temperature is now {value} degrees"

        // TODO: الاشتراك بمعالج آخر يطبع: "Logged: {value}"

        // TODO: تعيين درجة الحرارة إلى temp1

        // TODO: تعيين درجة الحرارة إلى temp2
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming