CodeGym /Kurslar /C# SELF /“Publisher-Subscriber” şablonu və C#-də hadisələr (

“Publisher-Subscriber” şablonu və C#-də hadisələr ( event)

C# SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Proqramlarla işləyərkən mütləq hallarla qarşılaşırsınız ki, bir hissə digər hissələri nəyinsə baş verdiyi barədə “xəbərdar” etməlidir. Klassik nümunə — istifadəçi mausla kliklədi və bu hadisə emal tələb edir. Gündəlik həyatda da biz hadisələr dünyasında yaşayırıq: mətbəxdə çaydanşı ulayanda — siqnalı eşidib sobanı söndürməyə tələsirsiniz. Qəhvə klaviaturaya töküldü — ürək hopdu — və siz noutbuku xilas etməyə qaçırsınız. Proqramlaşdırma da eyni qaydalara uyğundur.

Hadisə — bu, mənbə-obyektin (publisher) baş vermiş dəyişikliklər və ya əməliyyatlar barədə digər obyektləri (subscriber) xəbərdar etməsinə imkan verən mexanizmdir. Bir növ “mən elan etdim — kim diqqət yetirdi, o reaksiya verdi”.

C#-də hadisələr delegat tipi əsasında xüsusi konstruksiya olaraq mövcuddur. Delegat callback-ın imzasını təyin edir — abunəçilərdə nə və necə çağırılacaq. Hadisəni elan etmək üçün event açar sözü, delegat üçün isə delegate açar sözü istifadə olunur.

Niyə hadisələr lazımdır?

  • Zəif bağlılıq: Publisher abunəçilər barədə heç nə bilmir — yalnız siqnal verir.
  • Arxitektura elastikliyi: Handlerləri dinamik əlavə edib silmək olur, publisher-in kodunu dəyişməyə ehtiyac yoxdur.
  • Miqyaslana bilir: Yeni subscriber əlavə elədin — dərhal bildirişlər alacaq.

Klassik “Publisher-Subscriber” şablonu

Təsəvvür edək ki, bizim “Yanğın siqnalı” (publisher) sinfi və “Binadakı insan” (subscriber) sinfi var. Siqnal işə düşəndə o, hamıya eyni anda xəbər verir — binada neçə nəfər və harada olduqları vacib deyil. Bu, Publisher-Subscriber (və ya Observer) şablonudur.

Publisher neçə və hansı abunəçilərin olduğunu bilmir — o sadəcə xəbər verir, digərləri isə özləri abunə olurlar və ya görməzdən gəlirlər.

C#-də bu necə işləyir?

  • Publisher: hadisəni (event) müəyyən edir, abunə/abunəliyin ləğvi imkanını təmin edir.
  • Subscriber: hadisəyə abunə olur və handler-i reallaşdırır (hadisə baş verəndə bu metod çağırılacaq).

2. Hadisələr praktikada: ilk nümunə

Kod tərəfə keçəndə sadə modeli təsvir edək. Məsələn, konsol tətbiqimiz var, burada timer obyekt hər saniyə “tik” edir və müxtəlif handler-lər reaksiya verir (məsələn, “tik”i konsola yazırlar və ya tik sayını hesablayırlar).

Addım 1. Delegat və hadisəni müəyyən edək


public class SimpleTimer
{
    // Hadisə üçün delegat elan edək
    public delegate void TickEventHandler(object sender, EventArgs e);

    // Delegata əsaslanan hadisə
    public event TickEventHandler Tick;

    public void Start(int count)
    {
        for (int i = 0; i < count; i++)
        {
            System.Threading.Thread.Sleep(1000); // tiki simulyasiya edirik!
            OnTick(); // hadisəni yandırırıq!
        }
    }

    protected virtual void OnTick()
    {
        // Hadisənin abunəçiləri varsa çağırırıq (Tick != null)
        Tick?.Invoke(this, EventArgs.Empty);
    }
}

Burada nə baş verir?

  • TickEventHandler adlı delegat klasik imza ilə müəyyən edilib: object sender, EventArgs e.
  • Tick hadisəsi — handler-lərin abunə nöqtəsidir.
  • Start metodu “tik”ləməni simulə edir və periodik olaraq OnTick-i çağırır.
  • OnTick-də hadisə təhlükəsiz şəkildə çağırılır: Tick?.Invoke(..., EventArgs.Empty).

Addım 2. Hadisəyə abunə olmaq


class Program
{
    static void Main()
    {
        var timer = new SimpleTimer();

        // Tick hadisəsinə abunə oluruq
        timer.Tick += Timer_Tick;

        timer.Start(3);

        // Lazımdırsa abunəlikdən çıxa bilərik
        timer.Tick -= Timer_Tick;
    }

    static void Timer_Tick(object sender, EventArgs e)
    {
        Console.WriteLine("Tik!");
    }
}

Timer yaradırıq, += operatoru ilə abunə oluruq, sonra hər tikdə handler çağırılır. Abunəliyin ləğvi — -= operatoru.

3. Faydalı nüanslar

Niyə hadisələr “sərt” çağırışlardan daha yaxşıdır?

Əgər SimpleTimer OnTick-də birbaşa konsola yazsaydı, sinf konkret əməliyyatla sərt bağlı olardı. Hadisələr isə kodu “azad edir”: timer abunəçilərin nə edəcəyini bilmir — raketi işə salacaqlar, fayla log yazacaqlar yoxsa e-mail göndərəcəklər, bu onun işinə qarışmır.

Hadisə və delegat arasındakı vacib fərq

  • Delegat metodun “göstəricisi”dir, hadisə isə məhdudiyyətləri olan delegatdır.
  • Subscriber-lər yalnız abunə ola və ya abunəliyi ləğv edə bilərlər; xaricdən hadisəni çağırmaq olmaz — bunu yalnız publisher özü edə bilər.
  • Hadisəni elan etmək üçün delegat tipinə event modifikatorunu əlavə edin — compiler düzgün erişim modelini təmin edəcək.

C#-də hadisənin işləmə sxemi qısa


+------------------+          +------------------------------+
|                  |          |                              |
|     Publisher    | <------> |         Subscriber           |
| (Publisher/Event)|          |      (Subscriber/Handler)    |
|                  |          |                              |
+------------------+          +------------------------------+
         | 1) hadisəni elan edir      | 2) ona abunə olur
         | 3) onu çağırır             | 4) handler-i reallaşdırır

Hadisələri nə vaxt istifadə etmək lazımdır?

  • Məlumatı baş vermişdən sonra naməlum sayda dinləyiciyə çatdırmaq lazımdır.
  • Source sinfinin daxilindəki əməliyyatları sıx bağlı etmək istəmirsinizsə.
  • UI, asynchronous qarşılıqlı əlaqə, sistem bildirişləri — hamısı hadisələr ətrafında qurulur.

C#-də hadisələrin tətbiqi barədə qısa xüsusiyyətlər

  • Hadisəni xaricdən çağırmaq olmaz: yalnız publisher kodu Invoke edə bilər.
  • Abunə/abunəliyin ləğvi: operatorlar +=/-=; bir neçə handler ola bilər.
  • Hadisə əslində delegatlar siyahısıdır: hadisə baş verəndə bütün handler-lər abunəlik sırasına görə çağırılır.
  • Tövsiyə olunan delegatlar: uyğunluq üçün .NET ekosistemi ilə işləyişdə EventHandlerEventHandler<TEventArgs> istifadə edin.

4. Yeni başlayanların tipik səhvləri

Abunəçilərin olub-olmadığını yoxlamağı unuturlar: Tick != null. Təhlükəsiz çağırış üçün Tick?.Invoke(...) istifadə etmək daha yaxşıdır.

Hadisəyə abunə olurlar, amma handler artıq lazım olmadığı halda abunəliyi ləğv etmirlər. Bu obyektləri yaddaşda saxlayıb memory leak-ə gətirib çıxara bilər.

Xarici sinfdən hadisəni “çağırmağa” çalışırlar — compiler buna icazə verməyəcək. Hadisənin metod olmadığını, ona bənzər çağırış yazmağın mümkün olmadığını unutmayın.

Delegatın imzasına riayət etmirlər. Hadisələr üçün standart EventHandler və ya EventHandler<TEventArgs> istifadə edin — beləliklə kod .NET kitabxanaları ilə uyğun olacaq.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION