CẬP NHẬT
Kể từ C # 6, câu trả lời cho câu hỏi này là:
SomeEvent?.Invoke(this, e);
Tôi thường xuyên nghe / đọc lời khuyên sau:
Luôn tạo một bản sao của một sự kiện trước khi bạn kiểm tra null
và kích hoạt nó. Điều này sẽ loại bỏ một vấn đề tiềm ẩn với việc xâu chuỗi nơi sự kiện trở thành null
tại địa điểm ngay giữa nơi bạn kiểm tra null và nơi bạn phát sinh sự kiện:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Đã cập nhật : Tôi nghĩ từ việc đọc về tối ưu hóa rằng điều này cũng có thể yêu cầu thành viên sự kiện biến động, nhưng Jon Skeet nói trong câu trả lời của mình rằng CLR không tối ưu hóa bản sao.
Nhưng trong khi đó, để vấn đề này thậm chí xảy ra, một luồng khác phải làm một cái gì đó như thế này:
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
Trình tự thực tế có thể là hỗn hợp này:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Vấn đề là OnTheEvent
chạy theo tác giả đã hủy đăng ký, và họ chỉ hủy đăng ký cụ thể để tránh điều đó xảy ra. Chắc chắn những gì thực sự cần thiết là một triển khai sự kiện tùy chỉnh với sự đồng bộ hóa phù hợp trong add
và các bộ truy cập remove
. Và ngoài ra, có vấn đề về sự bế tắc có thể xảy ra nếu khóa được giữ trong khi một sự kiện được bắn.
Vì vậy, đây là lập trình Cult Cult ? Có vẻ như vậy - rất nhiều người phải thực hiện bước này để bảo vệ mã của họ khỏi nhiều luồng, trong thực tế, dường như đối với tôi, các sự kiện đòi hỏi nhiều sự quan tâm hơn trước khi chúng có thể được sử dụng như một phần của thiết kế đa luồng . Do đó, những người không thực hiện sự chăm sóc bổ sung đó cũng có thể bỏ qua lời khuyên này - đơn giản đó không phải là vấn đề đối với các chương trình đơn luồng, và trên thực tế, do không có volatile
hầu hết mã ví dụ trực tuyến, lời khuyên có thể không có có tác dụng gì cả.
(Và không đơn giản hơn nhiều khi chỉ gán chỗ trống delegate { }
trên tờ khai thành viên để bạn không bao giờ cần phải kiểm tra null
ngay từ đầu?)
Cập nhật:Trong trường hợp không rõ ràng, tôi đã nắm được ý định của lời khuyên - để tránh ngoại lệ tham chiếu null trong mọi trường hợp. Quan điểm của tôi là ngoại lệ tham chiếu null đặc biệt này chỉ có thể xảy ra nếu một luồng khác bị hủy khỏi sự kiện và lý do duy nhất để làm điều đó là để đảm bảo rằng sẽ không nhận được cuộc gọi nào nữa thông qua sự kiện đó, điều này rõ ràng KHÔNG đạt được bằng kỹ thuật này . Bạn sẽ che giấu một điều kiện cuộc đua - sẽ tốt hơn để tiết lộ nó! Ngoại lệ null đó giúp phát hiện sự lạm dụng thành phần của bạn. Nếu bạn muốn thành phần của mình được bảo vệ khỏi lạm dụng, bạn có thể làm theo ví dụ của WPF - lưu ID luồng trong hàm tạo của bạn và sau đó ném ngoại lệ nếu một luồng khác cố gắng tương tác trực tiếp với thành phần của bạn. Hoặc khác thực hiện một thành phần thực sự an toàn chủ đề (không phải là một nhiệm vụ dễ dàng).
Vì vậy, tôi cho rằng chỉ đơn thuần thực hiện bản sao / kiểm tra thành ngữ này là lập trình sùng bái hàng hóa, thêm lộn xộn và tiếng ồn vào mã của bạn. Để thực sự bảo vệ chống lại các chủ đề khác đòi hỏi nhiều công việc hơn.
Cập nhật để phản hồi các bài đăng trên blog của Eric Lippert:
Vì vậy, có một điều quan trọng mà tôi đã bỏ lỡ về các trình xử lý sự kiện: "các trình xử lý sự kiện được yêu cầu phải mạnh mẽ khi được gọi ngay cả sau khi sự kiện đã bị hủy đăng ký", và do đó chúng ta chỉ cần quan tâm đến khả năng của sự kiện đại biểu được null
. Là yêu cầu trên xử lý sự kiện tài liệu bất cứ nơi nào?
Và vì vậy: "Có nhiều cách khác để giải quyết vấn đề này, ví dụ: khởi tạo trình xử lý để có một hành động trống không bao giờ bị xóa. Nhưng thực hiện kiểm tra null là mẫu chuẩn."
Vì vậy, phần còn lại của câu hỏi của tôi là, tại sao rõ ràng-null-kiểm tra "mẫu chuẩn"? Giải pháp thay thế, chỉ định đại biểu trống, chỉ yêu cầu = delegate {}
được thêm vào tuyên bố sự kiện và điều này giúp loại bỏ những đống lễ nghi hôi thối từ mọi nơi diễn ra sự kiện. Sẽ thật dễ dàng để đảm bảo rằng đại biểu trống có giá rẻ để khởi tạo. Hay tôi vẫn còn thiếu một cái gì đó?
Chắc chắn đó phải là (như Jon Skeet đã đề xuất) đây chỉ là lời khuyên .NET 1.x chưa chết, như nó nên được thực hiện vào năm 2005?
EventName(arguments)
gọi đại biểu của sự kiện một cách vô điều kiện, thay vì chỉ gọi đại biểu nếu không null (không làm gì nếu null).