Trong trường hợp của bạn, mọi thứ đều ổn. Đó là đối tượng xuất bản các sự kiện giữ cho các mục tiêu của trình xử lý sự kiện hoạt động. Vì vậy, nếu tôi có:
publisher.SomeEvent += target.DoSomething;
sau đó publisher
có một tham chiếu đến target
nhưng không phải theo chiều ngược lại.
Trong trường hợp của bạn, nhà xuất bản sẽ đủ điều kiện để thu thập rác (giả sử không có tham chiếu nào khác đến nó) vì vậy thực tế là nó có tham chiếu đến các mục tiêu xử lý sự kiện là không liên quan.
Trường hợp khó khăn là khi nhà xuất bản tồn tại lâu dài nhưng người đăng ký không muốn như vậy - trong trường hợp đó, bạn cần hủy đăng ký người xử lý. Ví dụ: giả sử bạn có một số dịch vụ truyền dữ liệu cho phép bạn đăng ký nhận thông báo không đồng bộ về các thay đổi băng thông và đối tượng dịch vụ truyền có tuổi thọ lâu dài. Nếu chúng tôi làm điều này:
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;
(Bạn thực sự muốn sử dụng một khối cuối cùng để đảm bảo bạn không làm rò rỉ trình xử lý sự kiện.) Nếu chúng tôi không hủy đăng ký, thì khối BandwidthUI
sẽ tồn tại ít nhất là miễn là dịch vụ chuyển.
Cá nhân tôi hiếm khi gặp phải điều này - thường nếu tôi đăng ký một sự kiện, mục tiêu của sự kiện đó tồn tại ít nhất là miễn là nhà xuất bản - ví dụ: một biểu mẫu sẽ tồn tại miễn là nút trên đó. Thật đáng để biết về vấn đề tiềm ẩn này, nhưng tôi nghĩ một số người lo lắng về nó khi họ không cần thiết, bởi vì họ không biết các tham chiếu sẽ đi theo con đường nào.
CHỈNH SỬA: Đây là để trả lời bình luận của Jonathan Dickinson. Đầu tiên, hãy xem các tài liệu cho Delegate.Equals (đối tượng) rõ ràng cung cấp hành vi bình đẳng.
Thứ hai, đây là một chương trình ngắn nhưng đầy đủ để cho thấy hoạt động hủy đăng ký:
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();
}
}
Các kết quả:
Raising Foo
Subscriber.FooHandler()
Raising Foo
No handlers
(Đã thử nghiệm trên Mono và .NET 3.5SP1.)
Chỉnh sửa thêm:
Điều này để chứng minh rằng một nhà xuất bản sự kiện có thể được thu thập trong khi vẫn còn các tham chiếu đến người đăng ký.
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);
}
}
Kết quả (trong .NET 3.5SP1; Mono có vẻ hoạt động hơi kỳ lạ ở đây. Sẽ xem xét điều đó một lúc):
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