2008-12-04 34 views
65

Không thể kích hoạt sự kiện trong C# mà không có trình xử lý nào được gắn vào nó. Vì vậy, trước mỗi cuộc gọi nó là cần thiết để kiểm tra xem sự kiện là null.Tạo trình xử lý sự kiện C# trống

if (MyEvent != null) { 
    MyEvent(param1, param2); 
} 

Tôi muốn giữ mã của mình càng sạch càng tốt và loại bỏ các kiểm tra rỗng đó. Tôi không nghĩ rằng nó sẽ ảnh hưởng đến hiệu suất rất nhiều, ít nhất là không phải trong trường hợp của tôi.

MyEvent(param1, param2); 

Hiện tại tôi giải quyết vấn đề này bằng cách thêm trình xử lý nội tuyến trống vào từng sự kiện theo cách thủ công. Đây là lỗi dễ xảy ra, vì tôi cần nhớ để làm điều đó, vv

void Initialize() { 
    MyEvent += new MyEvent((p1,p2) => { }); 
} 

Có cách nào để tạo bộ xử lý trống cho tất cả các sự kiện của một lớp nhất định không?

+0

các trick trong các câu trả lời được chấp nhận sẽ tránh được việc phải kiểm tra null nhưng sẽ không đảm bảo an toàn bộ chủ đề. xem tại đây: http://stackoverflow.com/questions/1131184/c-initializing-an-event-handler-with-a-dummy/1131204#1131204 –

Trả lời

135

Tôi thấy điều này trên bài khác và đã trơ trẽn đánh cắp nó và sử dụng nó trong nhiều mã của tôi kể từ đó:

public delegate void MyClickHandler(object sender, string myValue); 
public event MyClickHandler Click = delegate {}; // add empty delegate! 

//Let you do this: 
public void DoSomething() { 
    Click(this, "foo"); 
} 

//Instead of this: 
public void DoSomething() { 
    if (Click != null) // Unnecessary! 
     Click(this, "foo"); 
} 

* Nếu có ai biết nguồn gốc của kỹ thuật này, xin vui lòng gửi nó trong các ý kiến . Tôi thực sự tin vào nguồn tín dụng nhận được.

(Edit: tôi đã nhận nó từ bài này Hidden Features of C#?)

+2

24 giây nhanh hơn! – leppie

+3

Thêm đại biểu trống, ngay tại đó! Điều đó thậm chí còn tốt hơn tôi mong đợi. Cảm ơn! Tôi sẽ đọc bài "ẩn tính năng" ngay bây giờ. –

+0

Có - Bài đăng đó là vô giá! Hãy chắc chắn bỏ phiếu thường xuyên ở đó. Họ đã làm tất cả chúng tôi một dịch vụ tuyệt vời. – Dinah

6

Bạn có thể viết là như:

MyEvent += delegate { }; 

Tôi không chắc chắn những gì bạn muốn làm là đúng.

+0

Tôi thực sự tin rằng việc thêm đại biểu trống vào MỌI sự kiện là đúng cách, cách phát triển ứng dụng. Nhưng tôi tin rằng, có thể có tình huống, đâu là giải pháp nhanh chóng và dễ dàng như thế nào để xử lý một cái gì đó. – TcKs

57

Ký hiệu:

if (MyEvent != null) { 
    MyEvent(param1, param2); 
} 

không phải là thread an toàn. Bạn nên làm theo cách này:

EventHandler handler = this.MyEvent; 
if (null != handler) { handler(param1, param2); } 

Tôi hiểu, rằng đây là một bận tâm, vì vậy bạn có thể thực hiện phương pháp helper:

static void RaiseEvent(EventHandler handler, object sender, EventArgs e) { 
    if (null != handler) { handler(sender, e); } 
} 

và sau đó gọi:

RaiseEvent(MyEvent, param1, param2); 

Nếu bạn đang sử dụng C# 3.0, bạn có thể khai báo phương thức trợ giúp dưới dạng phương thức mở rộng:

static void Raise(this EventHandler handler, object sender, EventArgs e) { 
    if (null != handler) { handler(sender, e); } 
} 

và sau đó gọi:

MyEvent.Raise(param1, param2); 

Ngoài ra bạn có thể tạo mở rộng tiếp theo/phương pháp helper cho xử lý sự kiện khác. Ví dụ:

static void Raise<TEventArgs>(this EventHandler<TEventArgs> handler, 
    object sender, TEventArgs e) where TEventArgs : EventArgs 
{ 
    if (null != handler) { handler(sender, e); } 
} 
+4

Sử dụng một phương pháp mở rộng là một giải pháp tuyệt vời. Tôi cringe khi khái niệm khởi tạo một đại biểu trống được đưa ra. – Greg

+0

Wow, tôi nghĩ rằng '= delegate {}' rất tiện dụng khi tôi nhìn thấy nó lần đầu tiên. Đây là 1 tuyệt vời, mặc dù. Và rất rõ ràng với sự hiểu biết, chết tiệt :) – shambulator

+0

+1 cho các phương pháp Mở rộng –

2

Đây là một ý tưởng tồi trong đó mã đang tiêu thụ sự kiện hiện có kỳ vọng đối tượng có sự kiện đã được mã hóa bằng hành động theo mặc định. Nếu mã của bạn sẽ không bao giờ được sử dụng ở bất cứ nơi nào khác bởi bất cứ ai khác thì tôi đoán bạn có thể thoát khỏi nó.

+0

Tôi đồng ý, như tôi đã nhận xét về câu trả lời của leppie. +1 – TcKs

5

Bạn không cần một số phương pháp mở rộng cho xử lý sự kiện khác nhau, bạn chỉ cần một:

public static class EventHandlerExtensions { 
    public static void Raise<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs { 
    if (handler != null) handler(sender, args); 
    } 
} 
+0

Cảm ơn bạn Sandro! – vkelman

-1

Bạn có thể sử dụng PostSharp để về thời gian xây dựng thêm kỳ diệu này. Đó là cách tốt nhất.

1

Rất tiếc, việc khai báo sự kiện C# bao gồm một số vấn đề về an toàn và sự thiếu hiệu quả. I designed a number of extension methods on delegates to invoke them safely, and to register/unregister delegates in a thread-safe manner.

cũ mã sự kiện-chăn của bạn:

if (someDelegate != null) someDelegate(x, y, z); 

mã mới của bạn:

someDelegate.Raise(x, y, z); 

cũ mã số đăng ký sự kiện của bạn:

event Action fooEvent; 
... 
lock (someDummyObject) fooEvent += newHandler; 

mã mới của bạn:

Action fooEvent; 
... 
Events.Add(ref fooEvent, newHandler); 

Không cần khóa, không có đối tượng giả được chèn trình biên dịch được sử dụng để khóa các sự kiện.

2

Trong C# 6.0 không cần phải đi đến bất kỳ những độ dài để làm việc kiểm tra null, nhờ vào các nhà điều hành vô điều kiện ?.

The docs giải thích rằng gọi MyEvent?.Invoke(...) bản sự kiện cho một biến tạm thời, thực hiện các null kiểm tra, và nếu không null, hãy gọi số Invoke trên bản sao tạm thời. Điều này không nhất thiết phải là chủ đề an toàn theo mọi nghĩa, vì ai đó có thể đã thêm một sự kiện mới sau bản sao vào biến tạm thời, mà sẽ không được gọi. Nó đảm bảo bạn sẽ không gọi số Invoke trên giá trị mặc định.

Nói tóm lại:

public delegate void MyClickHandler(object sender, string myValue); 
public event MyClickHandler Click; 

public void DoSomething() { 
    Click?.Invoke(this, "foo"); 
} 
Các vấn đề liên quan