あなたの場合、すべて問題ありません。イベントハンドラーのターゲットをライブに保つイベントを発行するのはオブジェクトです。だから私が持っている場合:
publisher.SomeEvent += target.DoSomething;
その後publisher
への参照がありますtarget
が、その逆はありません。
あなたの場合、パブリッシャーはガベージ コレクションの対象になるため (他に参照がない場合)、イベント ハンドラー ターゲットへの参照があるという事実は関係ありません。
トリッキーなケースは、パブリッシャーが長命であるが、サブスクライバーがそうであることを望んでいない場合です。その場合、ハンドラーのサブスクライブを解除する必要があります。たとえば、帯域幅の変更に関する非同期通知をサブスクライブできるデータ転送サービスがあり、転送サービス オブジェクトの寿命が長いとします。これを行う場合:
BandwidthUI ui = new BandwidthUI();
transferService.BandwidthChanged += ui.HandleBandwidthChange;
// Suppose this blocks until the transfer is complete
transferService.Transfer(source, destination);
// We now have to unsusbcribe from the event
transferService.BandwidthChanged -= ui.HandleBandwidthChange;
(イベント ハンドラーをリークしないようにするために、実際には finally ブロックを使用することをお勧めします。) サブスクライブを解除しなかった場合、 はBandwidthUI
少なくとも転送サービスと同じくらい存続します。
個人的には、これに遭遇することはめったにありません。通常、イベントにサブスクライブすると、そのイベントのターゲットは、少なくとも発行者と同じくらい存続します。たとえば、フォームは、その上にあるボタンと同じくらい存続します。この潜在的な問題について知っておくことは価値がありますが、必要のないときに心配する人もいると思います。
編集:これは、Jonathan Dickinson のコメントに答えるものです。まず、同等の動作を明確に示すDelegate.Equals(object)のドキュメントを見てください。
次に、サブスクリプション解除の動作を示す短いが完全なプログラムを次に示します。
using System;
public class Publisher
{
public event EventHandler Foo;
public void RaiseFoo()
{
Console.WriteLine("Raising Foo");
EventHandler handler = Foo;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
else
{
Console.WriteLine("No handlers");
}
}
}
public class Subscriber
{
public void FooHandler(object sender, EventArgs e)
{
Console.WriteLine("Subscriber.FooHandler()");
}
}
public class Test
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.Foo += subscriber.FooHandler;
publisher.RaiseFoo();
publisher.Foo -= subscriber.FooHandler;
publisher.RaiseFoo();
}
}
結果:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Mono および .NET 3.5SP1 でテスト済み)。
さらに編集:
これは、サブスクライバーへの参照がまだある間にイベント パブリッシャーを収集できることを証明するためです。
using System;
public class Publisher
{
~Publisher()
{
Console.WriteLine("~Publisher");
Console.WriteLine("Foo==null ? {0}", Foo == null);
}
public event EventHandler Foo;
}
public class Subscriber
{
~Subscriber()
{
Console.WriteLine("~Subscriber");
}
public void FooHandler(object sender, EventArgs e) {}
}
public class Test
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.Foo += subscriber.FooHandler;
Console.WriteLine("No more refs to publisher, "
+ "but subscriber is alive");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End of Main method. Subscriber is about to "
+ "become eligible for collection");
GC.KeepAlive(subscriber);
}
}
結果 (.NET 3.5SP1 では、Mono はここで少し奇妙な動作をしているように見えます。しばらく調べます):
No more refs to publisher, but subscriber is alive
~Publisher
Foo==null ? False
End of Main method. Subscriber is about to become eligible for collection
~Subscriber