Mi rendo perfettamente conto che quello che sto proponendo non segue le linee guida .NET e, quindi, è probabilmente una cattiva idea solo per questo motivo. Tuttavia, vorrei considerare questo da due possibili prospettive:
(1) Devo considerare di utilizzarlo per il mio lavoro di sviluppo, che è al 100% per scopi interni.
(2) È questo un concetto che i progettisti del framework potrebbero considerare di modificare o aggiornare?
Sto pensando di utilizzare una firma di evento che utilizzi un "mittente" fortemente digitato, invece di digitarlo come "oggetto", che è l'attuale modello di progettazione .NET. Cioè, invece di utilizzare una firma di evento standard simile a questa:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
Sto valutando di utilizzare una firma di evento che utilizza un parametro "mittente" di tipo forte, come segue:
Per prima cosa, definisci un "StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Questo non è poi così diverso da un Action <TSender, TEventArgs>, ma facendo uso di StrongTypedEventHandler, applichiamo che TEventArgs deriva da System.EventArgs.
Successivamente, come esempio, possiamo utilizzare StrongTypedEventHandler in una classe di pubblicazione come segue:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
La disposizione di cui sopra consentirebbe agli abbonati di utilizzare un gestore di eventi di tipo forte che non richiede il casting:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
Mi rendo perfettamente conto che ciò rompe con il modello standard di gestione degli eventi .NET; tuttavia, tieni presente che la controvarianza consentirebbe a un abbonato di utilizzare una firma di gestione degli eventi tradizionale, se lo desidera:
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
Cioè, se un gestore di eventi ha bisogno di sottoscrivere eventi da tipi di oggetti disparati (o forse sconosciuti), il gestore potrebbe digitare il parametro "mittente" come "oggetto" per gestire l'intera ampiezza dei potenziali oggetti mittente.
Oltre a infrangere le convenzioni (che è qualcosa che non prendo alla leggera, credimi) non riesco a pensare ad alcun aspetto negativo di questo.
Potrebbero esserci alcuni problemi di conformità con CLS qui. Questo funziona in Visual Basic .NET 2008 al 100% (l'ho testato), ma credo che le versioni precedenti di Visual Basic .NET fino al 2005 non abbiano covarianza e controvarianza delegata. [Modifica: da allora l'ho provato, ed è confermato: VB.NET 2005 e versioni precedenti non possono gestirlo, ma VB.NET 2008 va bene al 100%. Vedi "Modifica # 2", di seguito.] Potrebbero esserci altri linguaggi .NET che hanno anche un problema con questo, non posso esserne sicuro.
Ma non mi vedo sviluppare per alcun linguaggio diverso da C # o Visual Basic .NET, e non mi dispiace limitarlo a C # e VB.NET per .NET Framework 3.0 e versioni successive. (Non potevo immaginare di tornare alla 2.0 a questo punto, ad essere onesti.)
Qualcun altro può pensare a un problema con questo? O questo semplicemente rompe con le convenzioni così tanto da far girare lo stomaco alle persone?
Ecco alcuni collegamenti correlati che ho trovato:
(1) Linee guida per la progettazione di eventi [MSDN 3.5]
(3) Pattern di firma dell'evento in .net [StackOverflow 2008]
Sono interessato all'opinione di chiunque e di tutti su questo ...
Grazie in anticipo,
Mike
Modifica n. 1: questo è in risposta al post di Tommy Carlier :
Ecco un esempio funzionante completo che mostra che sia i gestori di eventi di tipo forte che gli attuali gestori di eventi standard che utilizzano un parametro "mittente oggetto" possono coesistere con questo approccio. Puoi copiare e incollare il codice e provarlo:
namespace csScrap.GenericEventHandling
{
class PublisherEventArgs : EventArgs
{
// ...
}
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs());
}
}
}
class StrongTypedSubscriber
{
public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
}
}
class TraditionalSubscriber
{
public void SomeEventHandler(object sender, PublisherEventArgs e)
{
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
}
}
class Tester
{
public static void Main()
{
Publisher publisher = new Publisher();
StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();
publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;
publisher.OnSomeEvent();
}
}
}
Modifica n. 2: questo è in risposta alla dichiarazione di Andrew Hare riguardo a covarianza e controvarianza e come si applica qui. I delegati nel linguaggio C # hanno avuto covarianza e controvarianza per così tanto tempo che sembra semplicemente "intrinseco", ma non lo è. Potrebbe anche essere qualcosa che è abilitato in CLR, non lo so, ma Visual Basic .NET non ha ottenuto la capacità di covarianza e controvarianza per i suoi delegati fino a .NET Framework 3.0 (VB.NET 2008). Di conseguenza, Visual Basic.NET per .NET 2.0 e versioni precedenti non sarebbero in grado di utilizzare questo approccio.
Ad esempio, l'esempio sopra può essere tradotto in VB.NET come segue:
Namespace GenericEventHandling
Class PublisherEventArgs
Inherits EventArgs
' ...
' ...
End Class
<SerializableAttribute()> _
Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
(ByVal sender As TSender, ByVal e As TEventArgs)
Class Publisher
Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)
Public Sub OnSomeEvent()
RaiseEvent SomeEvent(Me, New PublisherEventArgs)
End Sub
End Class
Class StrongTypedSubscriber
Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
End Sub
End Class
Class TraditionalSubscriber
Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
End Sub
End Class
Class Tester
Public Shared Sub Main()
Dim publisher As Publisher = New Publisher
Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber
AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler
publisher.OnSomeEvent()
End Sub
End Class
End Namespace
VB.NET 2008 può eseguirlo al 100%. Ma ora l'ho testato su VB.NET 2005, giusto per essere sicuro, e non si compila, affermando:
Il metodo 'Public Sub SomeEventHandler (sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' non ha la stessa firma del delegato 'Delegate Sub StrongTypedEventHandler (Of TSender, TEventArgs As System.EventArgs) (sender As Publisher, egs) '
Fondamentalmente, i delegati sono invarianti nelle versioni VB.NET 2005 e precedenti. In realtà ho pensato a questa idea un paio di anni fa, ma l'incapacità di VB.NET di affrontarlo mi ha infastidito ... Ma ora sono passato saldamente a C # e VB.NET ora può gestirlo, quindi, beh, da qui questo post.
Modifica: aggiorna n. 3
Ok, lo sto usando con successo da un po 'di tempo. È davvero un bel sistema. Ho deciso di chiamare il mio "StrongTypedEventHandler" "GenericEventHandler", definito come segue:
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Oltre a questa ridenominazione, l'ho implementata esattamente come discusso sopra.
Fa inciampare sulla regola FxCop CA1009, che afferma:
"Per convenzione, gli eventi .NET hanno due parametri che specificano il mittente dell'evento e i dati dell'evento. Le firme del gestore eventi devono seguire questo formato: void MyEventHandler (object sender, EventArgs e). Il parametro 'sender' è sempre di tipo System.Object, anche se è possibile utilizzare un tipo più specifico. Il parametro "e" è sempre di tipo System.EventArgs. Gli eventi che non forniscono dati sugli eventi dovrebbero utilizzare il tipo di delegato System.EventHandler. I gestori di eventi restituiscono void in modo che possano inviare ogni evento a più metodi di destinazione. Qualsiasi valore restituito da un obiettivo andrebbe perso dopo la prima chiamata. "
Ovviamente sappiamo tutto questo e stiamo comunque infrangendo le regole. (Tutti i gestori di eventi possono utilizzare lo standard "Mittente oggetto" nella loro firma, se lo si preferisce in ogni caso - questa è una modifica senza interruzioni.)
Quindi l'uso di a SuppressMessageAttributefa il trucco:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
Spero che questo approccio diventi lo standard in futuro. Funziona davvero molto bene.
Grazie per tutte le vostre opinioni ragazzi, lo apprezzo davvero ...
Mike
oh hi this my hom work solve it plz :code dump:domande di dimensioni tweet , ma una domanda dalla quale impariamo .
EventHandler<,>quello GenericEventHandler<,>. Esiste già un generico EventHandler<>in BCL denominato semplicemente EventHandler. Quindi EventHandler è un nome più comune e i delegati supportano i sovraccarichi di tipo