Jeśli skonfiguruję wiele programów obsługi zdarzeń, na przykład:
_webservice.RetrieveDataCompleted += ProcessData1;
_webservice.RetrieveDataCompleted += ProcessData2;
w jakiej kolejności są uruchamiane programy obsługi, gdy zdarzenie RetrieveDataCompleted
jest uruchamiane? Czy są uruchamiane w tym samym wątku i sekwencyjnie w kolejności, w jakiej zostały zarejestrowane?
c#
asynchronous
event-handling
Phillip Ngan
źródło
źródło
Odpowiedzi:
Obecnie są one realizowane w kolejności, w jakiej zostały zarejestrowane. Jest to jednak szczegół implementacji i nie polegałbym na tym, że to zachowanie pozostanie takie samo w przyszłych wersjach, ponieważ nie jest to wymagane przez specyfikacje.
źródło
Stąd: klasa delegatów
źródło
add
iremove
Hasła nie muszą być wdrażane jako delegat grupowej zdarzenie.Możesz zmienić kolejność, odłączając wszystkie programy obsługi, a następnie podłączając je ponownie w żądanej kolejności.
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]; } } }
źródło
Kolejność jest dowolna. Nie można polegać na tym, że programy obsługi będą wykonywane w określonej kolejności od jednego wywołania do następnego.
Edycja: A także - chyba że jest to tylko z ciekawości - fakt, że musisz wiedzieć, wskazuje na poważny problem projektowy.
źródło
Są uruchamiane w kolejności, w jakiej zostały zarejestrowane.
RetrieveDataCompleted
jest delegatem multiemisji . Patrzę przez reflektor, aby spróbować zweryfikować, i wygląda na to, że tablica jest używana za kulisami, aby śledzić wszystko.źródło
Jeśli ktoś musi to zrobić w kontekście System.Windows.Forms.Form, oto przykład odwrócenia kolejności zdarzenia 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]); } } } } }
źródło
MulticastDelegate ma połączoną listę delegatów, nazywaną listą wywołań, składającą się z co najmniej jednego elementu. Gdy delegat multiemisji jest wywoływany, delegaci na liście wywołań są wywoływani synchronicznie w kolejności, w jakiej się pojawiają. Jeśli podczas wykonywania listy wystąpi błąd, zostanie zgłoszony wyjątek.
źródło
Ale nikt nie mówi, że lista wywołań utrzymuje delegatów w tej samej kolejności, w jakiej są dodawani. W ten sposób kolejność wywołań nie jest gwarantowana.
źródło
Jest to funkcja, która umieści nową funkcję obsługi zdarzeń w dowolnym miejscu na liście wywołań z wieloma bramkami.
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); }
Następnie zawsze możesz usunąć funkcję za pomocą „- =” w dowolnym miejscu w kodzie.
PS - nie wykonuję żadnej obsługi błędów dla parametru „pozycja”.
źródło
Miałem podobny problem. W moim przypadku naprawiono to bardzo łatwo. Nigdy nie widziałem delegata, który nie używałby operatora + =. Mój problem został rozwiązany, ponieważ jeden delegat był zawsze dodawany na końcu, a wszyscy inni są zawsze dodawani na początku. Przykład PO wyglądałby tak:
W pierwszym przypadku ProcessData1 zostanie wywołany jako ostatni. W drugim przypadku ProcessData2 zostanie wywołana jako pierwsza.
źródło