0Pricing
C# Academy · レッスン

イベントパターンと注意点(メモリリーク)

イベントの注意点を見つけて修正します。メモリリークを避けるために購読を解除し、解除用にdelegateの参照を保持し、IDisposableによる購読パターンを使います。

「イベントパターンと注意点(メモリリーク)」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応の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、または明示的なメソッド)の責任を持つようにします。
  • 削除できないインラインの匿名ハンドラーで購読することは避けます。
  • static イベントには注意してください。オブジェクトより長く存続することがよくあります。
  • ハンドラーは短く保ち、発行側をブロックしないでください。

イベントによるリークの原因

簡単な確認です。C# イベントの使用時にメモリリークを引き起こす一般的な原因は何でしょうか?

まとめ

まとめ: リークを防ぐために購読を解除し、解除できるようにハンドラーへの参照を保存し、安全なイベントのライフタイム管理には破棄可能なパターンまたは finally を使用するパターンを優先します。

よくある質問

「イベントパターンと注意点(メモリリーク)」レッスンは無料ですか?

はい。「イベントパターンと注意点(メモリリーク)」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全3レッスンが含まれています。

「イベントパターンと注意点(メモリリーク)」で何を学びますか?

イベントの注意点を見つけて修正します。メモリリークを避けるために購読を解除し、解除用にdelegateの参照を保持し、IDisposableによる購読パターンを使います。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応の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に戻る