이벤트 핸들러 실행 순서


93

다음과 같이 여러 이벤트 핸들러를 설정하면 :

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

이벤트 RetrieveDataCompleted가 시작될 때 핸들러는 어떤 순서로 실행 됩니까? 동일한 스레드에서 등록 된 순서대로 순차적으로 실행됩니까?


2
대답은 RetrieveDataCompleted 이벤트에 따라 다릅니다. 멀티 캐스트 델리게이트의 기본 백업 저장소가있는 경우 "동일한 스레드에서 등록 된 순서대로 순차적으로 실행"됩니다.
HappyNomad

답변:


131

현재 등록 된 순서대로 실행됩니다. 그러나 이것은 구현 세부 사항이며 사양에 필요하지 않으므로 향후 버전에서 동일하게 유지되는이 동작에 의존하지 않을 것입니다.


5
왜 반대표를 던 졌는지 궁금합니다. 이것이 바로 진정한, 그리고 ... 직접 질문에 대한 대답
리드 Copsey

2
@Rawling : 이벤트 처리가 아닌 이항 연산자 오버로드 해결을위한 것입니다. 이 경우 더하기 연산자가 아닙니다.
Reed Copsey

2
아, 내가 어디로 잘못 가고 있는지 알 수 있습니다. "이벤트 핸들러는 델리게이트 죠?" 나는 지금 그들이 아니라는 것을 압니다. 자신에게 증명하기 위해 처리기를 역순으로 실행하는 이벤트를 직접 작성했습니다. :)
Rawling

16
명확히하기 위해 주문은 특정 이벤트에 대한 백업 저장소에 따라 다릅니다. 이벤트에 대한 기본 백업 저장소 인 멀티 캐스트 대리자는 등록 순서대로 실행되는 것으로 문서화됩니다. 향후 프레임 워크 버전에서는 변경되지 않습니다. 변경 될 수있는 것은 특정 이벤트에 사용되는 백업 저장소입니다.
HappyNomad

6
2 점에서 실제로 부정확하므로 반대표를 던졌습니다. 1) 현재 특정 이벤트 의 구현이 지시 하는 순서대로 실행됩니다. 이벤트에 대한 자체 추가 / 제거 메소드를 구현할 수 있기 때문입니다. 2) 멀티 캐스트 델리게이트를 통해 기본 이벤트 구현을 사용할 때 실제로는 사양에 따라 순서가 필요합니다.
Søren Boisen

53

대리자의 호출 목록은 목록의 각 요소가 대리자가 호출 한 메서드 중 정확히 하나를 호출하는 순서가 지정된 대리자 집합입니다. 호출 목록에는 중복 메소드가 포함될 수 있습니다. 호출 중에 델리게이트는 호출 목록에 나타나는 순서대로 메서드를 호출합니다 .

여기에서 : Delegate Class


1
좋지만 addremove키워드를 사용하면 이벤트가 반드시 멀티 캐스트 델리게이트로 구현되는 것은 아닙니다.
HappyNomad

에서와 같이 밥의 다른 답변 맞아하거나,이 대답은 너무 그것에 대해 얘기 할 수 여부를 이벤트 핸들러와 이것의 사용은 신뢰할로 이동해야 뭔가 ...이라고 언급.
n611x007 2014 년

12

모든 핸들러를 분리 한 다음 원하는 순서로 다시 연결하여 순서를 변경할 수 있습니다.

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

순서는 임의적입니다. 하나의 호출에서 다음 호출까지 특정 순서로 실행되는 핸들러에 의존 할 수 없습니다.

편집 : 또한-이것이 호기심에서 벗어나지 않는 한-알아야 할 사실은 심각한 디자인 문제를 나타냅니다 .


3
순서는 특정 이벤트의 구현에 따라 다르지만 임의적 이지는 않습니다 . 이벤트의 문서에 호출 순서가 명시되어 있지 않는 한, 나는 그것에 의존하는 것이 위험하다는 데 동의합니다. 그런 맥락에서 나는 후속 질문을 올렸다 .
HappyNomad

9
다른 클래스에서 부분적인 순서로 처리해야하는 이벤트를 갖는 것은 나에게 심각한 디자인 문제로 보이지 않습니다. 이벤트 등록이 순서 나 이벤트를 알기 어렵게하여 순서가 중요하다는 것을 알면 문제가 발생합니다.
Ignacio Soler Garcia

8

등록 된 순서대로 실행됩니다. RetrieveDataCompletedA는 멀티 캐스트 대표 . 나는 반사경을 통해 확인하고 시도하고 있으며 모든 것을 추적하기 위해 배후에서 배열이 사용되는 것처럼 보입니다.


다른 답변은 이벤트 핸들러를 사용하면 '우연한', '깨지기 쉬운', '구현 세부 사항'등입니다. 표준이나 관례에 의해 요구되지 않고 그냥 발생합니다. 맞습니까? 어쨌든이 답변은 그것도 참조 할 수 있습니다.
n611x007 2014-06-27

3

누군가 System.Windows.Forms.Form의 컨텍스트에서이 작업을 수행해야하는 경우 Shown 이벤트의 순서를 반전하는 예제가 있습니다.

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에는 하나 이상의 요소로 구성된 호출 목록이라고하는 델리게이트 연결 목록이 있습니다. 멀티 캐스트 대리자가 호출되면 호출 목록의 대리자가 나타나는 순서대로 동 기적으로 호출됩니다. 목록 실행 중에 오류가 발생하면 예외가 발생합니다.


2

호출 중에 메서드는 호출 목록에 나타나는 순서대로 호출됩니다.

그러나 아무도 호출 목록이 추가 된 것과 동일한 순서로 델리게이트를 유지한다고 말하지 않습니다. 따라서 호출 순서는 보장되지 않습니다.


1

다중 델리게이트 호출 목록에서 원하는 위치에 새 이벤트 처리기 함수를 배치하는 함수입니다.

    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);
    }

그런 다음 코드에서 편리한 곳이면 언제든지 '-='로 함수를 제거 할 수 있습니다.

추신- '위치'매개 변수에 대해 오류 처리를하지 않습니다.


0

비슷한 문제가있었습니다. 제 경우에는 아주 쉽게 고쳐졌습니다. + = 연산자를 사용하지 않은 델리게이트는 본 적이 없습니다. 내 문제는 항상 마지막에 한 명의 대리인을 추가하고 다른 모든 대리인은 항상 처음에 추가함으로써 수정되었습니다. OP의 예는 다음과 같습니다.

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

첫 번째 경우 ProcessData1이 마지막으로 호출됩니다. 두 번째 경우 ProcessData2가 먼저 호출됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.