0Pricing
C# Academy · درس

أنماط الأحداث ومشكلاتها (تسرّب الذاكرة)

اكتشف مشكلات الأحداث وأصلحها: ألغِ الاشتراك لتجنب تسرّب الذاكرة، واحتفظ بمراجع delegates لإزالتها، واستخدم نمط اشتراك يطبّق IDisposable.

أنماط الأحداث ومشكلاتها (تسرّب الذاكرة) درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 3 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 3 دروس في المجموع.

نظرة عامة على المحاذير

الهدف: استخدام الأحداث بأمان.

  • إلغاء الاشتراك لمنع تسرب الذاكرة
  • الاحتفاظ بمرجع إلى المعالج نفسه لإزالته
  • تفضيل نمط IDisposable صغير للتنظيف
  • تجنب المعالجات المجهولة المضمّنة التي لا يمكنك فصلها

إلغاء الاشتراك لتجنب التسرب

تحتفظ الأحداث بمراجع إلى المشتركين. إذا ظل الناشر موجودًا فترة طويلة ولم تفصل الاشتراك، فلن يمكن جمع المشترك.

using System;

public class Publisher
{
  public event EventHandler Tick;
  public void Fire()
  {
    EventHandler h = Tick;
    if (h != null) h(this, EventArgs.Empty);
  }
}

public class Subscriber
{
  public void Handle(object s, EventArgs e)
  {
    Console.WriteLine("Handling tick");
  }

  public void Subscribe(Publisher p) { p.Tick += Handle; }
  public void Unsubscribe(Publisher p) { p.Tick -= Handle; } // required to avoid leaks
}

public class Program
{
  public static void Main(string[] args)
  {
    Publisher p = new Publisher();
    Subscriber s = new Subscriber();

    s.Subscribe(p);
    p.Fire();                // prints once

    s.Unsubscribe(p);        // detach when done
    s = null;                // now eligible for GC in a real app
    p.Fire();                // no output
  }
}

الاحتفاظ بمرجع المعالج

لفصل معالج مجهول، خزّنه في متغير ومرّر المثيل نفسه إلى -=.

using System;

public class Button
{
  public event EventHandler Click;
  public void Simulate()
  {
    EventHandler h = Click;
    if (h != null) h(this, EventArgs.Empty);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Button b = new Button();

    // BAD: cannot easily remove this later
    // b.Click += (s, e) => Console.WriteLine("inline");

    // GOOD: store the handler, attach, later detach the same instance
    EventHandler saved = (s, e) => Console.WriteLine("saved");
    b.Click += saved;

    b.Simulate();           // prints "saved"
    b.Click -= saved;       // can remove because we kept the reference
    b.Simulate();           // no output
  }
}

اشتراك قابل للتخلص

أعِد IDisposable من Subscribe. استخدم using لإلغاء الاشتراك تلقائيًا.

using System;

public class Ticker
{
  public event EventHandler Tick;

  public IDisposable Subscribe(EventHandler handler)
  {
    Tick += handler;
    return new Unsubscriber(this, handler);
  }

  private sealed class Unsubscriber : IDisposable
  {
    private Ticker _t;
    private EventHandler _h;
    private bool _done;

    public Unsubscriber(Ticker t, EventHandler h) { _t = t; _h = h; }

    public void Dispose()
    {
      if (!_done)
      {
        _t.Tick -= _h;
        _done = true;
      }
    }
  }

  public void Fire()
  {
    EventHandler h = Tick;
    if (h != null) h(this, EventArgs.Empty);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Ticker t = new Ticker();
    using (t.Subscribe((s, e) => Console.WriteLine("tick")))
    {
      t.Fire(); // prints once
    }
    t.Fire();   // nothing after disposal
  }
}

الفصل باستخدام try/finally

استخدم try/finally عندما يجب فصل المعالج حتى إذا أُلقي استثناء.

using System;

public class Alarm
{
  public event EventHandler Ring;
  public void Trigger()
  {
    EventHandler h = Ring;
    if (h != null) h(this, EventArgs.Empty);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    Alarm a = new Alarm();
    EventHandler h = (s, e) => Console.WriteLine("ring");

    a.Ring += h;
    try
    {
      a.Trigger(); // do work with subscription active
    }
    finally
    {
      a.Ring -= h; // always detach
    }

    a.Trigger(); // no output
  }
}

نصائح لتجنب التسرب

نصائح:

  • ينبغي للمشتركين أن يتولوا إلغاء اشتراكهم (Dispose أو finally أو أسلوب صريح).
  • تجنب الاشتراك باستخدام معالجات مجهولة مضمّنة لا يمكنك إزالتها.
  • انتبه إلى static events؛ فهي غالبًا ما تبقى بعد انتهاء عمر الكائنات.
  • اجعل المعالجات قصيرة؛ ولا تحظر الناشر.

سبب التسرب مع الأحداث

تحقق سريع: ما السبب الشائع لتسرب الذاكرة عند استخدام أحداث C#؟

مراجعة

مراجعة: ألغِ الاشتراك لمنع التسرب، واحتفظ بمراجع المعالجات لإزالتها، وفضّل أنماط التنظيف القابلة للتخلص أو المعتمدة على finally لإدارة أعمار الأحداث بأمان.

الأسئلة الشائعة

هل درس «أنماط الأحداث ومشكلاتها (تسرّب الذاكرة)» مجاني؟

نعم — نص درس «أنماط الأحداث ومشكلاتها (تسرّب الذاكرة)» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 3 دروس في المجموع.

ماذا ستتعلم في «أنماط الأحداث ومشكلاتها (تسرّب الذاكرة)»؟

اكتشف مشكلات الأحداث وأصلحها: ألغِ الاشتراك لتجنب تسرّب الذاكرة، واحتفظ بمراجع delegates لإزالتها، واستخدم نمط اشتراك يطبّق IDisposable. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟

لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 3.

كم من الوقت يستغرق درس «أنماط الأحداث ومشكلاتها (تسرّب الذاكرة)»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟

نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. delegate وAction/Func والإغلاقات
  2. الأحداث: النشر والاشتراك، والكلمة المفتاحية event
  3. أنماط الأحداث ومشكلاتها (تسرّب الذاكرة)
← العودة إلى C# Academy