0Pricing
C# Academy · 课时

事件模式与常见陷阱(内存泄漏)

发现并修复事件陷阱:取消订阅以避免内存泄漏,保存委托引用以便移除,并使用 IDisposable 订阅模式。

事件模式与常见陷阱(内存泄漏) 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 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
  }
}

可释放的订阅

从 Subscribe 返回一个 IDisposable。使用 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 或显式方法)。
  • 避免使用无法移除的内联匿名处理程序进行订阅。
  • 小心静态事件;它们通常比对象存活更久。
  • 让处理程序保持简短;不要阻塞发布者。

事件导致泄漏的原因

快速检查:使用 C# 事件时,导致内存泄漏的常见原因是什么?

总结

总结:取消订阅以防止泄漏,存储处理程序引用以便移除处理程序,并优先使用可释放模式或基于 finally 的清理模式,从而安全地管理事件生命周期。

常见问题解答

「事件模式与常见陷阱(内存泄漏)」课时是免费的吗?

是的 — 「事件模式与常见陷阱(内存泄漏)」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 3 节课。

「事件模式与常见陷阱(内存泄漏)」这节课中我会学到什么?

发现并修复事件陷阱:取消订阅以避免内存泄漏,保存委托引用以便移除,并使用 IDisposable 订阅模式。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C# Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。

「事件模式与常见陷阱(内存泄漏)」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C# Academy 课中编写并运行代码吗?

能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. delegate、Action/Func 与闭包
  2. 事件:发布/订阅与 event 关键字
  3. 事件模式与常见陷阱(内存泄漏)
← 返回 C# Academy