다음과 같이 여러 이벤트 핸들러를 설정하면 :
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;
이벤트 RetrieveDataCompleted
가 시작될 때 핸들러는 어떤 순서로 실행 됩니까? 동일한 스레드에서 등록 된 순서대로 순차적으로 실행됩니까?
다음과 같이 여러 이벤트 핸들러를 설정하면 :
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;
이벤트 RetrieveDataCompleted
가 시작될 때 핸들러는 어떤 순서로 실행 됩니까? 동일한 스레드에서 등록 된 순서대로 순차적으로 실행됩니까?
답변:
현재 등록 된 순서대로 실행됩니다. 그러나 이것은 구현 세부 사항이며 사양에 필요하지 않으므로 향후 버전에서 동일하게 유지되는이 동작에 의존하지 않을 것입니다.
대리자의 호출 목록은 목록의 각 요소가 대리자가 호출 한 메서드 중 정확히 하나를 호출하는 순서가 지정된 대리자 집합입니다. 호출 목록에는 중복 메소드가 포함될 수 있습니다. 호출 중에 델리게이트는 호출 목록에 나타나는 순서대로 메서드를 호출합니다 .
여기에서 : Delegate Class
add
및 remove
키워드를 사용하면 이벤트가 반드시 멀티 캐스트 델리게이트로 구현되는 것은 아닙니다.
모든 핸들러를 분리 한 다음 원하는 순서로 다시 연결하여 순서를 변경할 수 있습니다.
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];
}
}
}
순서는 임의적입니다. 하나의 호출에서 다음 호출까지 특정 순서로 실행되는 핸들러에 의존 할 수 없습니다.
편집 : 또한-이것이 호기심에서 벗어나지 않는 한-알아야 할 사실은 심각한 디자인 문제를 나타냅니다 .
등록 된 순서대로 실행됩니다. RetrieveDataCompleted
A는 멀티 캐스트 대표 . 나는 반사경을 통해 확인하고 시도하고 있으며 모든 것을 추적하기 위해 배후에서 배열이 사용되는 것처럼 보입니다.
누군가 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]);
}
}
}
}
}
다중 델리게이트 호출 목록에서 원하는 위치에 새 이벤트 처리기 함수를 배치하는 함수입니다.
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);
}
그런 다음 코드에서 편리한 곳이면 언제든지 '-='로 함수를 제거 할 수 있습니다.
추신- '위치'매개 변수에 대해 오류 처리를하지 않습니다.
비슷한 문제가있었습니다. 제 경우에는 아주 쉽게 고쳐졌습니다. + = 연산자를 사용하지 않은 델리게이트는 본 적이 없습니다. 내 문제는 항상 마지막에 한 명의 대리인을 추가하고 다른 모든 대리인은 항상 처음에 추가함으로써 수정되었습니다. OP의 예는 다음과 같습니다.
_webservice.RetrieveDataCompleted = _webservice.RetrieveDataCompleted + ProcessData1;
_webservice.RetrieveDataCompleted = ProcessData2 + _webservice.RetrieveDataCompleted;
첫 번째 경우 ProcessData1이 마지막으로 호출됩니다. 두 번째 경우 ProcessData2가 먼저 호출됩니다.