Condividi tramite


Modello di evento .NET Core aggiornato

precedente

L'articolo precedente ha illustrato i modelli di eventi più comuni. .NET Core ha un modello più rilassato. In questa versione la definizione di EventHandler<TEventArgs> non ha più il vincolo che TEventArgs deve essere una classe derivata da System.EventArgs.

Questo aumenta la flessibilità per te ed è compatibile con le versioni precedenti. Iniziamo con la flessibilità. L'implementazione per System.EventArgs usa un metodo definito in System.Object un metodo: MemberwiseClone(), che crea una copia superficiale dell'oggetto. Tale metodo deve usare la reflection per implementare la relativa funzionalità per qualsiasi classe derivata da EventArgs. Questa funzionalità è più semplice da creare in una classe derivata specifica. Ciò significa che la derivazione da System.EventArgs è un vincolo che limita le progettazioni, ma non offre alcun vantaggio aggiuntivo. In effetti, è possibile modificare le definizioni di FileFoundArgs e SearchDirectoryArgs in modo che non derivino da EventArgs. Il programma funziona esattamente allo stesso modo.

È anche possibile modificare il SearchDirectoryArgs in uno struct, se si apporta un'altra modifica:

internal struct SearchDirectoryArgs
{
    internal string CurrentSearchDirectory { get; }
    internal int TotalDirs { get; }
    internal int CompletedDirs { get; }

    internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
    {
        CurrentSearchDirectory = dir;
        TotalDirs = totalDirs;
        CompletedDirs = completedDirs;
    }
}

La modifica aggiuntiva consiste nel chiamare il costruttore senza parametri prima di immettere il costruttore che inizializza tutti i campi. Senza questa aggiunta, le regole di C# segnalano che le proprietà sono accessibili prima di essere assegnate.

Non è consigliabile modificare il FileFoundArgs da una classe (tipo riferimento) a uno struct (tipo valore). Il protocollo per la gestione dell'annullamento richiede di passare gli argomenti dell'evento per riferimento. Se è stata apportata la stessa modifica, la classe di ricerca file non può mai osservare le modifiche apportate da uno dei sottoscrittori di eventi. Una nuova copia della struttura verrà usata per ogni sottoscrittore e tale copia sarà una copia diversa da quella visualizzata dall'oggetto di ricerca file.

Si esaminerà ora come questa modifica può essere compatibile con le versioni precedenti. La rimozione del vincolo non influisce su alcun codice esistente. Qualsiasi tipo di argomento evento esistente deriva comunque da System.EventArgs. La compatibilità con le versioni precedenti è uno dei motivi principali per cui continuano a derivare da System.EventArgs. Tutti i sottoscrittori di eventi esistenti sono sottoscrittori di un evento che ha seguito il modello classico.

Seguendo una logica simile, qualsiasi tipo di argomento di evento creato ora non avrà ascoltatori in nessuna codebase esistente. I nuovi tipi di evento che non derivano da System.EventArgs non interrompono tali codebase.

Eventi con sottoscrittori asincroni

Hai un ultimo modello da imparare: come scrivere correttamente i sottoscrittori di eventi che chiamano codice asincrono. La sfida è descritta nell'articolo su async e await. I metodi asincroni possono avere un tipo di ritorno void, ma ciò è sconsigliato. Quando il codice del sottoscrittore di eventi chiama un metodo asincrono, non hai altra scelta che creare un metodo async void. La firma del gestore eventi lo richiede.

È necessario riconciliare questa guida opposta. In qualche modo, dovete creare un metodo sicuro async void. I concetti di base del modello da implementare sono illustrati nel codice seguente:

worker.StartWorking += async (sender, eventArgs) =>
{
    try
    {
        await DoWorkAsync();
    }
    catch (Exception e)
    {
        //Some form of logging.
        Console.WriteLine($"Async task failure: {e.ToString()}");
        // Consider gracefully, and quickly exiting.
    }
};

Prima di tutto, si noti che il gestore è contrassegnato come gestore asincrono. Poiché viene assegnato a un tipo delegato del gestore eventi, ha un tipo di ritorno void. Ciò significa che è necessario seguire il modello della funzione gestore e non consentire che alcuna eccezione venga lanciata fuori dal contesto del gestore asincrono. Poiché non restituisce un'attività, non esiste alcuna attività in grado di segnalare l'errore immettendo lo stato di errore. Poiché il metodo è asincrono, il metodo non può generare l'eccezione. Il metodo chiamante continua l'esecuzione perché è async. Il comportamento effettivo del runtime è definito in modo diverso per ambienti diversi. Potrebbe terminare il thread o il processo a cui appartiene il thread, o lasciare il processo in uno stato indeterminato. Tutti questi potenziali risultati sono estremamente indesiderati.

È consigliabile racchiudere l'espressione await per il Task asincrono nel proprio blocco try. Se ciò provoca un'attività con errore, è possibile registrare l'errore. Se si tratta di un errore da cui l'applicazione non è in grado di eseguire il ripristino, è possibile uscire rapidamente e normalmente dal programma

Questo articolo ha illustrato gli aggiornamenti principali del modello di eventi .NET. Potrebbero essere visualizzati molti esempi delle versioni precedenti nelle librerie con cui si lavora. Tuttavia, è necessario comprendere anche quali sono i modelli più recenti. È possibile visualizzare il codice completato per l'esempio in Program.cs.

L'articolo successivo di questa serie consente di distinguere tra l'uso di delegates e events nei progetti. Sono concetti simili e questo articolo ti aiuta a prendere la decisione migliore per i tuoi programmi.

successivo