Thứ tự thực hiện trình xử lý sự kiện


93

Nếu tôi thiết lập nhiều trình xử lý sự kiện, như sau:

_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;

trình tự xử lý sẽ chạy khi sự kiện RetrieveDataCompletedđược kích hoạt? Chúng có chạy trong cùng một chủ đề và tuần tự theo thứ tự đã đăng ký không?


2
Câu trả lời sẽ dành riêng cho sự kiện RetrieveDataCompleted. Nếu nó có kho dự phòng mặc định của một đại biểu nhiều lớp, thì có "chúng chạy trong cùng một luồng và tuần tự theo thứ tự đã được đăng ký".
HappyNomad

Câu trả lời:


131

Hiện tại, chúng được thực hiện theo thứ tự đã đăng ký. Tuy nhiên, đây là chi tiết triển khai và tôi sẽ không dựa vào hành vi này sẽ giữ nguyên trong các phiên bản tương lai, vì nó không được yêu cầu bởi các thông số kỹ thuật.


5
Tôi tự hỏi, tại sao lại phản đối? Đây chính là sự thật, và trả lời câu hỏi trực tiếp ...
Reed Copsey

2
@Rawling: Đó là để giải quyết quá tải toán tử nhị phân - không phải xử lý sự kiện. Đây không phải là toán tử bổ sung, trong trường hợp này.
Reed Copsey

2
À, tôi thấy mình đang sai ở đâu: "Người xử lý sự kiện là người được ủy quyền, đúng không?". Bây giờ tôi biết họ không. Đã viết cho mình một sự kiện kích hoạt người xử lý theo thứ tự ngược lại, chỉ để chứng minh điều đó cho bản thân mình :)
Rawling

16
Để làm rõ, thứ tự phụ thuộc vào cửa hàng hỗ trợ cho một sự kiện cụ thể. Kho lưu trữ sao lưu mặc định cho các sự kiện, đại biểu nhiều lớp, được ghi nhận là đang thực thi theo thứ tự đăng ký. Điều này sẽ không thay đổi trong phiên bản khung trong tương lai. Điều có thể thay đổi là cửa hàng hỗ trợ được sử dụng cho một sự kiện cụ thể.
HappyNomad

6
Đã phản đối vì nó thực tế không chính xác trên 2 điểm. 1) Hiện tại chúng được thực thi theo thứ tự mà việc triển khai sự kiện cụ thể ra lệnh - vì bạn có thể triển khai các phương thức thêm / bớt của riêng mình cho các sự kiện. 2) Khi sử dụng triển khai sự kiện mặc định thông qua đại biểu nhiều lớp, thứ tự trên thực tế là yêu cầu của các đặc tả.
Søren Boisen

53

Danh sách gọi của một đại diện là một tập hợp các đại biểu có thứ tự, trong đó mỗi phần tử của danh sách gọi chính xác một trong các phương thức được gọi bởi đại biểu. Danh sách lời gọi có thể chứa các phương thức trùng lặp. Trong một lệnh gọi, một đại biểu gọi các phương thức theo thứ tự mà chúng xuất hiện trong danh sách lệnh gọi .

Từ đây: Lớp đại biểu


1
Tốt, nhưng sử dụng từ khóa addvà, removemột sự kiện có thể không nhất thiết phải được triển khai dưới dạng đại biểu nhiều thành phần.
HappyNomad

như với của Bob , các câu trả lời khác đề cập rằng việc sử dụng điều này với các trình xử lý sự kiện là điều không đáng tin cậy ... cho dù điều đó có đúng hay không, câu trả lời này cũng có thể nói về điều đó.
n611x007

12

Bạn có thể thay đổi thứ tự bằng cách tách tất cả các trình xử lý, sau đó gắn lại theo thứ tự mong muốn.

public event EventHandler event1;

public void ChangeHandlersOrdering()
{
    if (event1 != null)
    {
        List<EventHandler> invocationList = event1.GetInvocationList()
                                                  .OfType<EventHandler>()
                                                  .ToList();

        foreach (var handler in invocationList)
        {
            event1 -= handler;
        }

        //Change ordering now, for example in reverese order as follows
        for (int i = invocationList.Count - 1; i >= 0; i--)
        {
            event1 += invocationList[i];
        }
    }
}

10

Thứ tự là tùy ý. Bạn không thể dựa vào các trình xử lý đang được thực thi theo bất kỳ thứ tự cụ thể nào từ lệnh gọi này đến lệnh tiếp theo.

Chỉnh sửa: Và ngoài ra - trừ khi điều này chỉ vì tò mò - thực tế mà bạn cần biết là dấu hiệu của một vấn đề thiết kế nghiêm trọng .


3
Thứ tự phụ thuộc vào việc thực hiện sự kiện cụ thể, nhưng nó không phải là tùy ý. Tuy nhiên, trừ khi tài liệu của sự kiện chỉ ra thứ tự gọi, tôi đồng ý rằng sẽ rất rủi ro nếu phụ thuộc vào nó. Trong mạch đó, tôi đã đăng một câu hỏi tiếp theo .
HappyNomad

9
Để có một sự kiện cần được xử lý bởi các lớp khác nhau theo thứ tự phân tử dường như không phải là một vấn đề thiết kế nghiêm trọng đối với tôi. Vấn đề sẽ xảy ra nếu các đăng ký sự kiện được thực hiện theo cách khó biết thứ tự hoặc sự kiện biết rằng thứ tự là quan trọng.
Ignacio Soler Garcia

8

Chúng được chạy theo thứ tự mà chúng được đăng ký. RetrieveDataCompletedlà một Đại biểu Multicast . Tôi đang nhìn qua gương phản xạ để thử và xác minh, và có vẻ như một mảng được sử dụng ở hậu trường để theo dõi mọi thứ.


các câu trả lời khác lưu ý rằng với trình xử lý sự kiện, đây là 'tình cờ', 'dễ vỡ', 'chi tiết triển khai', v.v., tức là. không được yêu cầu bởi bất kỳ tiêu chuẩn hay quy ước nào, nó chỉ xảy ra. Có đúng không? trong mọi trường hợp, câu trả lời này cũng có thể đề cập đến điều đó.
n611x007

3

Nếu ai đó cần làm điều này trong ngữ cảnh của System.Windows.Forms.Form, đây là một ví dụ về đảo ngược thứ tự của sự kiện Hiển thị.

using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace ConsoleApplication {
    class Program {
        static void Main() {
            Form form;

            form = createForm();
            form.ShowDialog();

            form = createForm();
            invertShownOrder(form);
            form.ShowDialog();
        }

        static Form createForm() {
            var form = new Form();
            form.Shown += (sender, args) => { Console.WriteLine("form_Shown1"); };
            form.Shown += (sender, args) => { Console.WriteLine("form_Shown2"); };
            return form;
        }

        static void invertShownOrder(Form form) {
            var events = typeof(Form)
                .GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(form, null) as EventHandlerList;

            var shownEventKey = typeof(Form)
                .GetField("EVENT_SHOWN", BindingFlags.NonPublic | BindingFlags.Static)
                .GetValue(form);

            var shownEventHandler = events[shownEventKey] as EventHandler;

            if (shownEventHandler != null) {
                var invocationList = shownEventHandler
                    .GetInvocationList()
                    .OfType<EventHandler>()
                    .ToList();

                foreach (var handler in invocationList) {
                    events.RemoveHandler(shownEventKey, handler);
                }

                for (int i = invocationList.Count - 1; i >= 0; i--) {
                    events.AddHandler(shownEventKey, invocationList[i]);
                }
            }
        }
    }
}

2

MulticastDelegate có một danh sách các đại biểu được liên kết, được gọi là danh sách gọi, bao gồm một hoặc nhiều phần tử. Khi một đại biểu đa hướng được gọi, các đại biểu trong danh sách triệu gọi được gọi đồng bộ theo thứ tự mà chúng xuất hiện. Nếu một lỗi xảy ra trong quá trình thực thi danh sách thì một ngoại lệ sẽ được ném ra.


2

Trong một lệnh gọi, các phương thức được gọi theo thứ tự mà chúng xuất hiện trong danh sách lệnh gọi.

Nhưng không ai nói rằng danh sách lệnh gọi duy trì các đại biểu theo thứ tự như khi chúng được thêm vào. Do đó, thứ tự yêu cầu không được đảm bảo.


1

Đây là một hàm sẽ đặt hàm xử lý sự kiện mới ở bất cứ đâu bạn muốn trong danh sách lệnh gọi đa điểm.

    private void addDelegateAt(ref YourDelegate initial, YourDelegate newHandler, int position)
    {
        Delegate[] subscribers = initial.GetInvocationList();
        Delegate[] newSubscriptions = new Delegate[subscribers.Length + 1];

        for (int i = 0; i < newSubscriptions.Length; i++)
        {
            if (i < position)
                newSubscriptions[i] = subscribers[i];
            else if (i==position)
                newSubscriptions[i] = (YourDelegate)newHandler;
            else if (i > position)
                newSubscriptions[i] = subscribers[i-1];
        }

        initial = (YourDelegate)Delegate.Combine(newSubscriptions);
    }

Sau đó, bạn luôn có thể xóa hàm bằng dấu '- =' ở bất kỳ nơi nào thuận tiện trong mã của bạn.

Tái bút - Tôi không thực hiện bất kỳ xử lý lỗi nào đối với tham số 'position'.


0

Tôi đã có một vấn đề tương tự. Trong trường hợp của tôi, nó đã được sửa rất dễ dàng. Tôi chưa bao giờ thấy một đại biểu không sử dụng toán tử + =. Sự cố của tôi đã được khắc phục bằng cách luôn thêm một đại biểu vào cuối, tất cả những người khác luôn được thêm vào đầu. Ví dụ của OP sẽ như sau:

    _webservice.RetrieveDataCompleted = _webservice.RetrieveDataCompleted + ProcessData1;
    _webservice.RetrieveDataCompleted = ProcessData2 + _webservice.RetrieveDataCompleted;

Trong trường hợp đầu tiên, ProcessData1 sẽ được gọi sau cùng. Trong trường hợp thứ 2, ProcessData2 sẽ được gọi đầu tiên.

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.