Dalam kasus Anda, semuanya baik-baik saja. Itu adalah objek yang menerbitkan acara yang membuat target dari penangan acara tetap hidup. Jadi jika saya memiliki:
publisher.SomeEvent += target.DoSomething;
kemudian publisher
memiliki referensi ke target
tapi tidak sebaliknya.
Dalam kasus Anda, penerbit akan memenuhi syarat untuk pengumpulan sampah (dengan asumsi tidak ada referensi lain untuk itu) sehingga fakta bahwa itu mendapat referensi ke target pengendali kejadian tidak relevan.
Kasus rumitnya adalah ketika penerbit berumur panjang tetapi pelanggan tidak ingin menjadi - dalam hal ini Anda perlu berhenti berlangganan penangan. Misalnya, Anda memiliki beberapa layanan transfer data yang memungkinkan Anda berlangganan pemberitahuan asinkron tentang perubahan bandwidth, dan objek layanan transfer berumur panjang. Jika kita melakukan ini:
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;
(Anda sebenarnya ingin menggunakan blok terakhir untuk memastikan Anda tidak membocorkan penangan peristiwa.) Jika kami tidak berhenti berlangganan, maka BandwidthUI
akan berlaku setidaknya selama layanan transfer.
Secara pribadi saya jarang menemukan ini - biasanya jika saya berlangganan ke suatu acara, target acara itu hidup setidaknya selama penerbit - formulir akan bertahan selama tombol yang ada di atasnya, misalnya. Penting untuk mengetahui tentang potensi masalah ini, tetapi saya pikir beberapa orang khawatir tentang itu ketika mereka tidak membutuhkannya, karena mereka tidak tahu ke arah mana referensi tersebut mengarah.
EDIT: Ini untuk menjawab komentar Jonathan Dickinson. Pertama, lihat dokumen untuk Delegate.Equals (objek) yang secara jelas memberikan perilaku kesetaraan.
Kedua, berikut adalah program singkat tapi lengkap untuk menunjukkan cara berhenti berlangganan:
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();
}
}
Hasil:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Diuji di Mono dan .NET 3.5SP1.)
Edit lebih lanjut:
Hal ini untuk membuktikan bahwa event publisher bisa dihimpun selagi masih ada referensi ke subscriber.
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);
}
}
Hasil (dalam .NET 3.5SP1; Mono tampaknya berperilaku agak aneh di sini. Akan memeriksanya beberapa saat):
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
Dalam kasus Anda, Anda baik-baik saja. Saya awalnya membaca pertanyaan Anda secara terbalik, bahwa pelanggan berada di luar cakupan, bukan penerbit . Jika penerbit acara keluar dari ruang lingkup, maka referensi ke pelanggan (bukan pelanggan itu sendiri, tentu saja!) Ikut serta dan tidak perlu menghapusnya secara eksplisit.
Jawaban asli saya ada di bawah, tentang apa yang terjadi jika Anda membuat pelanggan acara dan membiarkannya keluar dari ruang lingkup tanpa berhenti berlangganan. Ini tidak berlaku untuk pertanyaan Anda tetapi saya akan membiarkannya di tempat untuk sejarah.
Jika kelas masih terdaftar melalui penangan acara, maka kelas masih bisa dijangkau. Itu masih benda hidup. GC yang mengikuti grafik peristiwa akan menemukannya terhubung. Ya, Anda pasti ingin menghapus event handler secara eksplisit.
Hanya karena objek di luar ruang lingkup alokasi aslinya tidak berarti itu adalah calon GC. Selama referensi langsung tetap ada, itu hidup.
sumber