あなたの場合、すべてが大丈夫です。イベントハンドラーのターゲットをライブに保つのは、イベントを公開するオブジェクトです。だから私が持っている場合:
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
ライブを解除しなかった場合は、少なくとも転送サービスと同じくらい存続します。
個人的に私がこれに遭遇することはめったにありません-通常、私がイベントをサブスクライブする場合、そのイベントのターゲットは、少なくともパブリッシャーと同じくらい存続します-たとえば、フォームは、その上にあるボタンと同じくらい持続します。この潜在的な問題について知っておく価値はありますが、参照がどの方向に進むかわからないため、必要のないときに心配する人もいると思います。
編集:これはジョナサンディキンソンのコメントに答えることです。まず、明らかに等価性の振る舞いを与える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