Ordine di esecuzione del gestore eventi


93

Se imposto più gestori di eventi, in questo modo:

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

in che ordine vengono eseguiti i gestori quando RetrieveDataCompletedviene generato l'evento ? Vengono eseguiti nello stesso thread e in sequenza nell'ordine in cui sono registrati?


2
La risposta sarà specifica per l'evento RetrieveDataCompleted. Se dispone dell'archivio di backup predefinito di un delegato multicast, sì "vengono eseguiti nello stesso thread e sequenzialmente nell'ordine in cui sono registrati".
HappyNomad

Risposte:


131

Attualmente vengono eseguiti nell'ordine in cui sono registrati. Tuttavia, questo è un dettaglio di implementazione e non farei affidamento sul fatto che questo comportamento rimanga lo stesso nelle versioni future, poiché non è richiesto dalle specifiche.


5
Mi chiedo, perché i voti negativi? Questo è esattamente vero e risponde direttamente alla domanda ...
Reed Copsey

2
@Rawling: è per la risoluzione dell'overload di operatori binari, non per la gestione degli eventi. Questo non è l'operatore di addizione, in questo caso.
Reed Copsey

2
Ah, vedo dove sto sbagliando: "I gestori di eventi sono delegati, giusto?". Ora so che non lo sono. Mi sono scritto un evento che licenzia i gestori in ordine inverso, solo per dimostrarlo a me stesso :)
Rawling

16
Per chiarire, l'ordine dipende dal negozio di supporto per un particolare evento. L'archivio di backup predefinito per gli eventi, i delegati multi-cast, sono documentati come eseguiti in ordine di registrazione. Questo non cambierà in una futura versione del framework. Ciò che può cambiare è il backing store utilizzato per un particolare evento.
HappyNomad

6
Downvoted poiché è effettivamente errato su 2 punti. 1) Attualmente vengono eseguiti nell'ordine dettato dall'implementazione dell'evento specifico, poiché è possibile implementare i propri metodi di aggiunta / rimozione per gli eventi. 2) Quando si utilizza l'implementazione dell'evento predefinito tramite delegati multi-cast, l'ordine è in effetti richiesto dalle specifiche.
Søren Boisen

53

L'elenco di chiamate di un delegato è un insieme ordinato di delegati in cui ogni elemento dell'elenco richiama esattamente uno dei metodi richiamati dal delegato. Un elenco di chiamate può contenere metodi duplicati. Durante una chiamata, un delegato richiama i metodi nell'ordine in cui vengono visualizzati nell'elenco di chiamate .

Da qui: Delegate Class


1
Bello, ma utilizzando le parole chiave adde removeun evento potrebbe non essere necessariamente implementato come delegato multi-cast.
HappyNomad

come con Bob's , le altre risposte menzionano che l'uso di questo con i gestori di eventi è qualcosa che dovrebbe essere considerato inaffidabile ... che sia giusto o no, questa risposta potrebbe parlare anche di questo.
n611x007

12

È possibile modificare l'ordine scollegando tutti i gestori e quindi ricollegandoli nell'ordine desiderato.

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

L'ordine è arbitrario. Non puoi fare affidamento sul fatto che i gestori vengano eseguiti in un ordine particolare da una chiamata all'altra.

Modifica: E anche, a meno che non sia solo per curiosità, il fatto che devi sapere è indicativo di un serio problema di progettazione.


3
L'ordine dipende dall'implementazione particolare dell'evento, ma è non arbitrario. A meno che la documentazione dell'evento non indichi l'ordine di chiamata, tuttavia, sono d'accordo che è rischioso dipendere da esso. In questo senso, ho pubblicato una domanda di follow-up .
HappyNomad

9
Avere un evento che deve essere gestito da classi diverse in un ordine parziale non mi sembra un serio problema di progettazione. Il problema si verificherà se le registrazioni all'evento vengono effettuate in un modo che rende difficile conoscere l'ordine o l'evento sapere che l'ordine è importante.
Ignacio Soler Garcia

8

Vengono eseguiti nell'ordine in cui sono registrati. RetrieveDataCompletedè un Multicast Delegates . Sto guardando attraverso il riflettore per provare a verificare e sembra che un array venga utilizzato dietro le quinte per tenere traccia di tutto.


le altre risposte notano che con i gestori di eventi questo è "accidentale", "fragile", "dettaglio di implementazione", ecc. non richiesto da alcuno standard o convenzione, succede e basta. è giusto? in ogni caso, questa risposta potrebbe riferirsi anche a quello.
n611x007

3

Se qualcuno ha bisogno di farlo nel contesto di un System.Windows.Forms.Form, ecco un esempio che inverte l'ordine dell'evento 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

Un MulticastDelegate dispone di un elenco collegato di delegati, denominato elenco di chiamate, costituito da uno o più elementi. Quando viene richiamato un delegato multicast, i delegati nell'elenco di chiamate vengono chiamati in modo sincrono nell'ordine in cui vengono visualizzati. Se si verifica un errore durante l'esecuzione dell'elenco, viene generata un'eccezione.


2

Durante una chiamata, i metodi vengono richiamati nell'ordine in cui sono visualizzati nell'elenco delle chiamate.

Ma nessuno dice che l'elenco di chiamate mantiene i delegati nello stesso ordine in cui vengono aggiunti. Pertanto, l'ordine di invocazione non è garantito.


1

Questa è una funzione che posizionerà la nuova funzione di gestore di eventi dove vuoi nell'elenco di chiamate a più eventi.

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

Quindi puoi sempre rimuovere la funzione con un '- =' ovunque nel tuo codice sia conveniente.

PS - Non sto eseguendo alcuna gestione degli errori per il parametro "position".


0

Ho avuto un problema simile. Nel mio caso è stato risolto molto facilmente. Non avevo mai visto un delegato che non usasse l'operatore + =. Il mio problema è stato risolto aggiungendo sempre un delegato alla fine, tutti gli altri vengono sempre aggiunti all'inizio. L'esempio dell'OP sarebbe qualcosa del tipo:

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

Nel primo caso ProcessData1 sarà chiamato per ultimo. Nel 2 ° caso ProcessData2 sarà chiamato per primo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.