ในกรณีของคุณทุกอย่างเรียบร้อยดี เป็นวัตถุที่เผยแพร่เหตุการณ์ที่ทำให้เป้าหมายของตัวจัดการเหตุการณ์ยังคงอยู่ ดังนั้นถ้าฉันมี:
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;
(คุณต้องการใช้การบล็อกในที่สุดเพื่อให้แน่ใจว่าคุณจะไม่รั่วไหลของตัวจัดการเหตุการณ์) หากเราไม่ได้ยกเลิกการสมัครบริการ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