C'è un modello standard per gli eventi in .NET: utilizzano un tipo delegate
che accetta un oggetto semplice chiamato mittente e quindi il "payload" effettivo in un secondo parametro, che deve essere derivato da EventArgs
.Cosa dovrei perdere abbandonando il modello standard EventHandler in .NET?
Il motivo per cui il secondo parametro è derivato da EventArgs
sembra piuttosto chiaro (vedere .NET Framework Standard Library Annotated Reference). Ha lo scopo di garantire la compatibilità binaria tra sink di eventi e sorgenti man mano che il software evolve. Per ogni evento, anche se ha solo un argomento, deriviamo una classe di argomenti evento personalizzata che ha una singola proprietà contenente quell'argomento, in questo modo conserviamo la possibilità di aggiungere più proprietà al carico utile nelle versioni future senza rompere il codice client esistente . Molto importante in un ecosistema di componenti sviluppati in modo indipendente.
Ma trovo che lo stesso vale per argomenti zero. Questo vuol dire che se ho un evento che non ha argomenti nella mia prima versione, e scrivo:
public event EventHandler Click;
... allora sto facendo male. Se cambio il tipo delegato in futuro di una nuova classe come il suo carico utile:
public class ClickEventArgs : EventArgs { ...
... spezzerò la compatibilità binaria con i miei clienti. Il client termina associato a un sovraccarico specifico di un metodo interno add_Click
che impiega EventHandler
e se si modifica il tipo di delegato, non è possibile trovare tale sovraccarico, quindi è disponibile un MissingMethodException
.
OK, quindi cosa succede se uso la versione generica a portata di mano?
public EventHandler<EventArgs> Click;
No, ancora sbagliata, perché un EventHandler<ClickEventArgs>
non è un EventHandler<EventArgs>
.
Quindi, per ottenere il vantaggio di EventArgs
, è possibile ottenere anziché utilizzarlo direttamente così com'è. Se non lo fai, potresti anche non usarlo (mi sembra).
Quindi c'è il primo argomento, sender
. Mi sembra una ricetta per l'accoppiamento empio. L'attivazione di un evento è essenzialmente una chiamata di funzione. Se la funzione, in generale, ha la capacità di tornare indietro nello stack e scoprire chi era il chiamante, e regolare il suo comportamento di conseguenza? Dovremmo mandare le interfacce a questo aspetto?
public interface IFoo
{
void Bar(object caller, int actualArg1, ...);
}
Dopo tutto, l'implementor di Bar
potrebbe desiderare di sapere chi è il caller
era, in modo che possano eseguire una query per ulteriori informazioni! Spero che tu ora stia vomitando. Perché dovrebbe essere diverso per gli eventi?
Quindi, anche se sono pronto a prendere il dolore di fare un autonomo EventArgs
classe -derived per ogni evento Dichiaro, proprio per rendere la pena mia mentre usando EventArgs
a tutti, ho sicuramente preferirei far cadere l'argomento object sender .
visiva funzione di autocompletamento di Studio non sembra cura ciò che delegato si utilizza per un evento - è possibile digitare +=
[premete Spazio, Return] e scrive un metodo di gestore per te, che le partite qualunque delegato Capita di essere .
Quindi quale valore dovrei perdere deviando dal modello standard?
Come domanda bonus, C#/CLR 4.0 farà qualcosa per cambiare questo, forse attraverso la contravarianza nei delegati? Ho provato a indagare su questo, ma ho colpito another problem. In origine avevo incluso questo aspetto della domanda in quell'altra domanda, ma lì causava confusione. E sembra un po 'troppo per dividere questo in su in un totale di tre domande ...
Aggiornamento:
Risulta ho fatto bene a interrogarsi sugli effetti del controvarianza su tutta questa faccenda!
Come noted elsewhere, le nuove regole del compilatore lasciano un buco nel sistema di tipi che esplode in fase di esecuzione. Il foro è stato effettivamente collegato definendo EventHandler<T>
in modo diverso a Action<T>
.
Quindi per gli eventi, per evitare quel tipo di foro non è necessario utilizzare Action<T>
. Ciò non significa che devi usare EventHandler<TEventArgs>
; significa semplicemente che se si utilizza un tipo di delegato generico, non selezionarne uno abilitato per controvarianza.
Essere interessati a sentire un motivo da downvoter. –
Non ho votato in entrambi i casi, ma sospetto che il downvote sia perché hai almeno due domande in una qui. Non voglio davvero provare ad affrontare il tutto da solo, ma sarei felice di guardare l'aspetto della varianza (come sto scrivendo in questo momento). Se potessi separare il bit che porta a un errore di esecuzione in una domanda separata con un caso breve ma completo per riprodurlo, sarei interessato ... –
Farà [IMBOTTITURA] –