Đã xử lý sự kiện đã được thêm?


183

Có cách nào để biết nếu một trình xử lý sự kiện đã được thêm vào một đối tượng không? Tôi sắp xếp một danh sách các đối tượng vào / ra khỏi trạng thái phiên để chúng ta có thể sử dụng trạng thái phiên dựa trên SQL ... Khi một đối tượng trong danh sách có thuộc tính thay đổi, nó cần được gắn cờ, mà trình xử lý sự kiện đã xử lý đúng cách trước đó . Tuy nhiên, bây giờ khi các đối tượng được khử lưu huỳnh, nó không nhận được xử lý sự kiện.

Trong một cơn khó chịu nhẹ, tôi chỉ cần thêm trình xử lý sự kiện vào thuộc tính Get để truy cập đối tượng. Bây giờ nó được gọi là tuyệt vời, ngoại trừ việc nó được gọi như thế 5 lần nên tôi nghĩ rằng trình xử lý cứ tiếp tục được thêm vào mỗi khi đối tượng được truy cập.

Nó thực sự đủ an toàn để bỏ qua, nhưng tôi muốn làm cho nó sạch hơn nhiều bằng cách kiểm tra xem liệu trình xử lý đã được thêm vào chưa, vì vậy tôi chỉ làm như vậy một lần.

Điều đó có thể không?

EDIT: Tôi không nhất thiết phải có toàn quyền kiểm soát những gì xử lý sự kiện được thêm vào, vì vậy chỉ cần kiểm tra null là không đủ.


Câu trả lời:


123

Từ bên ngoài lớp định nghĩa, như @Telos đề cập, bạn chỉ có thể sử dụng EventHandler ở phía bên trái của a +=hoặc a -=. Vì vậy, nếu bạn có khả năng sửa đổi lớp định nghĩa, bạn có thể cung cấp một phương thức để thực hiện kiểm tra bằng cách kiểm tra xem trình xử lý sự kiện là null- nếu vậy, thì không có trình xử lý sự kiện nào được thêm vào. Nếu không, thì có lẽ và bạn có thể lặp qua các giá trị trong Delegate.GetInvocationList . Nếu một người bằng với đại biểu mà bạn muốn thêm làm trình xử lý sự kiện, thì bạn biết đó là đại diện.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

Và điều này có thể dễ dàng được sửa đổi để trở thành "thêm trình xử lý nếu nó không ở đó". Nếu bạn không có quyền truy cập vào các bộ phận của lớp đang phơi bày sự kiện, bạn có thể cần khám phá -=+=, như được đề xuất bởi @Lou Franco.

Tuy nhiên, bạn có thể tốt hơn nên xem xét lại cách bạn vận hành và ngừng hoạt động các đối tượng này, để xem liệu bạn có thể tự tìm cách theo dõi thông tin này không.


7
Điều này không được biên dịch, EventHandler chỉ có thể ở phía bên trái của + = hoặc - =.
CodeRedick

2
Loại bỏ bỏ phiếu khi giải thích thêm. Trạng thái SQL phá hủy toàn bộ ý tưởng ở đây ... :(
CodeRedick

1
Cảm ơn Blair và tìm kiếm SO, đúng như những gì tôi đang tìm kiếm (phiền phức bạn không thể làm điều đó bên ngoài lớp học)
George Mauer

3
Vấn đề xảy ra hầu hết thời gian trong khi so sánh các đại biểu cho bình đẳng. Vì vậy, sử dụng Delegate.Equals(objA, objB)nếu bạn muốn kiểm tra chính xác sự tồn tại của đại biểu. Nếu không thì so sánh các thuộc tính riêng lẻ như thế nào if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay

2
Mã này không hoạt động trong WinForm. Có đúng với ASP.NET không?
jp2code

211

Gần đây tôi đã gặp một tình huống tương tự khi tôi cần phải đăng ký một người xử lý cho một sự kiện chỉ một lần. Tôi thấy rằng bạn có thể hủy đăng ký một cách an toàn trước, sau đó đăng ký lại, ngay cả khi trình xử lý chưa được đăng ký:

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

Lưu ý rằng làm điều này mỗi khi bạn đăng ký trình xử lý của bạn sẽ đảm bảo rằng trình xử lý của bạn chỉ được đăng ký một lần. Nghe có vẻ là một thực hành khá tốt với tôi :)


9
Có vẻ rủi ro; nếu một sự kiện được kích hoạt sau khi gỡ bỏ trình xử lý và trước khi thêm nó trở lại, nó sẽ bị bỏ qua.
Jimmy

27
Chắc chắn rồi. Bạn có nghĩa là nó không an toàn. Nhưng đó chỉ có thể là một vấn đề khi chạy nhiều luồng hoặc tương tự, điều không bình thường. Trong hầu hết các trường hợp, điều này là đủ tốt cho mục đích đơn giản.
alf

6
Điều này làm phiền tôi. Chỉ vì bạn hiện không rõ ràng tạo chủ đề trong mã của mình, điều đó không có nghĩa là không có nhiều chủ đề hoặc chúng sẽ không được thêm vào sau đó. Ngay sau khi bạn (hoặc ai đó trong nhóm, có thể vài tháng sau) thêm một luồng công nhân hoặc trả lời cả UI và kết nối mạng, điều này sẽ mở ra cơ hội cho các sự kiện bị gián đoạn rất cao.
Technophile

1
Tôi tin rằng cửa sổ thời gian giữa việc loại bỏ và thêm lại rất nhỏ, rất khó có thể tồn tại các sự kiện bị bỏ lỡ, nhưng vâng, nó vẫn có thể.
Alisson

2
Nếu bạn đang sử dụng cái này cho một cái gì đó như cập nhật UI vv thì một nhiệm vụ tầm thường như vậy là rủi ro. Nếu điều này là để xử lý gói mạng vv thì tôi sẽ không sử dụng nó.
cuộn

18

Nếu đây là trình xử lý duy nhất, bạn có thể kiểm tra xem sự kiện có phải là null không, nếu không, trình xử lý đã được thêm vào.

Tôi nghĩ rằng bạn có thể gọi một cách an toàn - = trên sự kiện với trình xử lý của bạn ngay cả khi nó không được thêm vào (nếu không, bạn có thể bắt nó) - để đảm bảo rằng nó không có trong đó trước khi thêm.


3
Logic này sẽ bị hỏng ngay khi sự kiện được xử lý ở một nơi khác.
lỗi87

6

Ví dụ này cho thấy cách sử dụng phương thức GetInvocationList () để truy xuất các đại biểu cho tất cả các trình xử lý đã được thêm vào. Nếu bạn đang tìm kiếm để xem nếu một trình xử lý cụ thể (hàm) đã được thêm vào thì bạn có thể sử dụng mảng.

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

Bạn có thể kiểm tra các thuộc tính khác nhau trên thuộc tính Phương thức của đại biểu để xem nếu một chức năng cụ thể đã được thêm vào.

Nếu bạn đang tìm kiếm để xem nếu chỉ có một đính kèm, bạn có thể kiểm tra null.


GetInvocationList () không phải là thành viên của lớp tôi. Trên thực tế, dường như tôi không thể tìm thấy phương thức này trên bất kỳ đối tượng hoặc trình xử lý nào mà tôi có quyền truy cập vào ...
CodeRedick

Tôi cũng đã thử điều đó, rõ ràng bạn chỉ có thể truy cập sự kiện như thế từ bên trong lớp. Tôi đang làm điều này một cách khái quát, và như những người khác đã đề cập đến những người xử lý sự kiện có lẽ đang bị lạc đường. Cảm ơn bạn đã làm rõ!
CodeRedick

4

Nếu tôi hiểu vấn đề của bạn một cách chính xác, bạn có thể có vấn đề lớn hơn. Bạn nói rằng các đối tượng khác có thể đăng ký vào các sự kiện này. Khi đối tượng được tuần tự hóa và giải tuần tự hóa, các đối tượng khác (những đối tượng mà bạn không có quyền kiểm soát) sẽ mất các trình xử lý sự kiện của chúng.

Nếu bạn không lo lắng về điều đó thì hãy giữ một tham chiếu đến trình xử lý sự kiện của bạn là đủ tốt. Nếu bạn lo lắng về tác dụng phụ của các đối tượng khác làm mất trình xử lý sự kiện của họ, thì bạn có thể muốn xem xét lại chiến lược bộ nhớ đệm của mình.


1
Ôi! Thậm chí còn không nghĩ về điều đó ... mặc dù điều đó rõ ràng khi xem xét vấn đề ban đầu của tôi là người xử lý của riêng tôi bị lạc.
CodeRedick

2

Cách duy nhất hiệu quả với tôi là tạo biến Boolean mà tôi đặt thành true khi tôi thêm sự kiện. Sau đó tôi hỏi: Nếu biến là sai, tôi thêm sự kiện.

bool alreadyAdded = false;

Biến này có thể là toàn cầu.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}

1

Tôi đồng ý với câu trả lời của alf, nhưng ít sửa đổi cho nó là ,, để sử dụng,

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }

0
EventHandler.GetInvocationList().Length > 0

2
không ném này khi danh sách == null?
Boris Callens

2
bên ngoài lớp sở hữu trình xử lý sự kiện, bạn chỉ có thể sử dụng - = và + =. bạn không thể truy cập sự kiện.
tbergelt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.