イベントパターンと注意点(メモリリーク)
イベントの注意点を見つけて修正します。メモリリークを避けるために購読を解除し、解除用に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 イベントには注意してください。オブジェクトより長く存続することがよくあります。
- ハンドラーは短く保ち、発行側をブロックしないでください。
イベントによるリークの原因
まとめ
まとめ: リークを防ぐために購読を解除し、解除できるようにハンドラーへの参照を保存し、安全なイベントのライフタイム管理には破棄可能なパターンまたは 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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- delegate、Action/Func、クロージャ
- イベント:発行と購読、eventキーワード
- イベントパターンと注意点(メモリリーク)