2012-04-23 16 views
12

Hầu hết các mã tôi đã thấy sử dụng cách sau để khai báo và gọi sự kiện bắn:Đây có phải là cách tốt hơn để kích hoạt/gọi các sự kiện mà không cần kiểm tra null trong C# không?

public class MyExample 
{ 
    public event Action MyEvent; // could be an event EventHandler<EventArgs>, too 

    private void OnMyEvent() 
    { 
     var handler = this.MyEvent; // copy before access (to aviod race cond.) 
     if (handler != null) 
     { 
      handler(); 
     } 
    } 

    public void DoSomeThingsAndFireEvent() 
    { 
     // ... doing some things here 
     OnMyEvent(); 
    } 
} 

Thậm chí ReSharper tạo ra một phương pháp cách gọi đường nêu trên.

Tại sao không chỉ làm điều đó theo cách này:

public class MyExample 
{ 
    public event Action MyEvent = delegate {}; // init here, so it's never null 

    public void DoSomeThingsAndFireEvent() 
    { 
     // ... doing some things here 
     OnMyEvent(); // save to call directly because this can't be null 
    } 
} 

bất cứ ai có thể giải thích lý do tại sao không làm điều này? (ủng hộ so với nhược điểm)

+0

Tôi nghe Jon Skeet thực hiện theo cả hai cách ... –

+0

Tôi nghĩ rằng trong hầu hết các trường hợp nó không quan trọng để có được nhận thấy nếu có ít nhất một "thực sự" nghe đính kèm ... như đã thấy ở trên đã có một kiểm tra null nếu được sử dụng theo cách "bình thường". – Beachwalker

+0

có thể trùng lặp của [Có một nhược điểm nào khi thêm một đại biểu trống ẩn danh vào khai báo sự kiện không?] (Http://stackoverflow.com/questions/170907/is-there-a-downside-to-adding-an-anonymous-empty -delate-on-event-declaration) – CodesInChaos

Trả lời

16

Các ưu và nhược điểm là:

  • kiểm tra null là cực kỳ rẻ; chúng tôi đang nói tỷ lệ phần trăm trong một giây. Phân bổ một đại biểu và sau đó thu gom rác không thành công trong suốt thời gian còn lại của đối tượng có thể có thể lên tới một vài số phần triệu trong một giây. Ngoài ra, bạn đang tiêu thụ hàng tá byte bộ nhớ khác. Ngoài ra, mỗi khi sự kiện xảy ra, bạn sẽ nhận được một cuộc gọi không cần thiết đến một phương thức không thực hiện gì, thậm chí tiêu tốn nhiều micro giây hơn. Nếu bạn là loại người quan tâm đến hàng triệu phần nghìn giây và hàng chục byte thì đó có thể là một sự khác biệt có ý nghĩa; cho phần lớn các trường hợp nó sẽ không.

  • Bạn phải nhớ luôn tạo đại biểu trống. Điều đó có thực sự dễ dàng hơn việc nhớ để kiểm tra null không?

  • Cả hai mẫu đều không thực sự làm cho sự kiện an toàn. Nó vẫn hoàn toàn có thể với cả hai mẫu cho một trình xử lý sự kiện được kích hoạt trên một luồng trong khi bị loại bỏ trên một luồng khác, và điều đó có nghĩa là chúng chạy đua. Nếu mã loại bỏ trình xử lý của bạn phá hủy trạng thái mà trình xử lý cần, thì có thể một luồng đang hủy trạng thái đó trong khi một luồng khác đang chạy trình xử lý. Đừng nghĩ rằng chỉ đơn thuần là kiểm tra null hoặc chỉ định một trình xử lý rỗng có thể loại bỏ một cách kỳ diệu các điều kiện chủng tộc. Nó chỉ loại bỏ các điều kiện chủng tộc mà kết quả trong dereferencing null.

+3

Không chỉ thêm bộ xử lý chất thải bộ xử lý trống trong trường hợp không có trình xử lý được sử dụng, nhưng quan trọng hơn là việc thêm và loại bỏ trình xử lý thực đầu tiên * nhiều * đắt hơn. Việc thêm trình xử lý đầu tiên vào một sự kiện rất rẻ: chỉ cần thay thế null bằng trình xử lý. Việc loại bỏ trình xử lý duy nhất khỏi một sự kiện là rẻ: thay thế nó bằng null. Tất cả các trường hợp khác đều đắt hơn khi so sánh. – supercat

+0

Có, một kiểm tra null là nhanh chóng nhưng không phải là bản sao của xử lý trước khi kiểm tra một hoạt động đắt tiền? Nó được thực hiện với mọi sự kiện cháy bằng cách gọi OnMyEvent(). Xem mã var handler = this.MyEvent; (trong ví dụ) Có sự so sánh thực tế "thực tế" nào có sẵn không? – Beachwalker

+1

@Stegi: Bản sao hoàn toàn miễn phí. Khi nó được nạp vào thanh ghi, nó đơn giản được giữ ở đó. (Ít nhất trong mã được tối ưu hóa) – configurator

5

có thể vẫn là giá trị rỗng. Xem xét:

var x = new MyExample(); 
    x.MyEvent += SomeHandler; 

    // ... later, when the above code is disposed of 

    x.MyEvent -= SomeHandler; 

EDIT: thực sự, tôi mang nó trở lại. Sau khi thử nghiệm điều này, nếu bạn đã sử dụng một đại biểu ẩn danh để đặt trình xử lý, có vẻ như bạn không có thể xóa nó, vì vậy bạn có thể lưu kiểm tra trống.

Tôi không chắc liệu đây có phải là hành vi đáng tin cậy hay không, hoặc chỉ là một tạo phẩm của việc triển khai ngôn ngữ ...

6

Đó là một điều chắc chắn; Tuy nhiên, tôi nghĩ rằng hầu hết các nhà phát triển đi cho an toàn hơn phong cách khi nói đến kiểm tra null. Không có gì trong thế giới này có thể đảm bảo rằng một lỗi sẽ không leo vào hệ thống và kiểm tra null sẽ tiếp tục là không cần thiết.