Je me rends parfaitement compte que ce que je propose ne suit pas les directives .NET et, par conséquent, est probablement une mauvaise idée pour cette seule raison. Cependant, je voudrais considérer cela sous deux angles possibles:
(1) Dois-je envisager de l'utiliser pour mon propre travail de développement, qui est à 100% à des fins internes.
(2) Est-ce un concept que les concepteurs du cadre pourraient envisager de modifier ou de mettre à jour?
Je pense utiliser une signature d'événement qui utilise un «expéditeur» fortement typé, au lieu de le taper comme «objet», qui est le modèle de conception actuel de .NET. Autrement dit, au lieu d'utiliser une signature d'événement standard qui ressemble à ceci:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
J'envisage d'utiliser une signature d'événement qui utilise un paramètre `` expéditeur '' de type fort, comme suit:
Tout d'abord, définissez un "StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Ce n'est pas si différent d'une Action <TSender, TEventArgs>, mais en utilisant le StrongTypedEventHandler
, nous imposons que le TEventArgs dérive System.EventArgs
.
Ensuite, à titre d'exemple, nous pouvons utiliser StrongTypedEventHandler dans une classe de publication comme suit:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
L'agencement ci-dessus permettrait aux abonnés d'utiliser un gestionnaire d'événements à fort typage qui ne nécessitait pas de diffusion:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
Je me rends parfaitement compte que cela rompt avec le modèle standard de gestion des événements .NET; cependant, gardez à l'esprit que la contravariance permettrait à un abonné d'utiliser une signature de gestion d'événements traditionnelle s'il le souhaite:
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
Autrement dit, si un gestionnaire d'événements avait besoin de s'abonner à des événements de types d'objets disparates (ou peut-être inconnus), le gestionnaire pourrait taper le paramètre «expéditeur» comme «objet» afin de gérer toute l'étendue des objets expéditeurs potentiels.
À part briser les conventions (ce que je ne prends pas à la légère, croyez-moi), je ne peux pas penser à des inconvénients à cela.
Il peut y avoir des problèmes de conformité CLS ici. Cela fonctionne à 100% dans Visual Basic .NET 2008 (j'ai testé), mais je crois que les anciennes versions de Visual Basic .NET jusqu'en 2005 n'ont pas de covariance déléguée et de contravariance. [Edit: J'ai depuis testé ceci, et c'est confirmé: VB.NET 2005 et ci-dessous ne peuvent pas gérer cela, mais VB.NET 2008 est 100% bien. Voir «Édition # 2», ci-dessous.] Il peut y avoir d'autres langages .NET qui ont également un problème avec cela, je ne peux pas être sûr.
Mais je ne me vois pas développer pour un langage autre que C # ou Visual Basic .NET, et cela ne me dérange pas de le limiter à C # et VB.NET pour .NET Framework 3.0 et supérieur. (Je ne pouvais pas imaginer revenir à 2.0 à ce stade, pour être honnête.)
Quelqu'un d'autre peut-il penser à un problème avec cela? Ou est-ce que cela rompt tellement avec les conventions que cela fait tourner l'estomac des gens?
Voici quelques liens connexes que j'ai trouvés:
(1) Directives de conception d'événements [MSDN 3.5]
(2) C # simple Event Raising - en utilisant «expéditeur» vs EventArgs personnalisés [StackOverflow 2009]
(3) Modèle de signature d'événement dans .net [StackOverflow 2008]
Je suis intéressé par l'opinion de tout le monde à ce sujet ...
Merci d'avance,
Mike
Edit # 1: Ceci est en réponse au message de Tommy Carlier :
Voici un exemple de travail complet qui montre que les gestionnaires d'événements à typage fort et les gestionnaires d'événements standard actuels qui utilisent un paramètre 'object sender' peuvent coexister avec cette approche. Vous pouvez copier-coller le code et lui donner une exécution:
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();
}
}
}
Edit # 2: Ceci est en réponse à la déclaration d'Andrew Hare concernant la covariance et la contravariance et comment cela s'applique ici. Les délégués dans le langage C # ont eu la covariance et la contravariance pendant si longtemps que cela semble juste "intrinsèque", mais ce n'est pas le cas. C'est peut-être même quelque chose qui est activé dans le CLR, je ne sais pas, mais Visual Basic .NET n'a pas obtenu de capacité de covariance et de contravariance pour ses délégués avant le .NET Framework 3.0 (VB.NET 2008). Et par conséquent, Visual Basic.NET pour .NET 2.0 et les versions antérieures ne pourraient pas utiliser cette approche.
Par exemple, l'exemple ci-dessus peut être traduit en VB.NET comme suit:
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 peut l'exécuter à 100%. Mais je l'ai maintenant testé sur VB.NET 2005, juste pour être sûr, et il ne se compile pas, déclarant:
La méthode 'Public Sub SomeEventHandler (sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' n'a pas la même signature que le délégué 'Delegate Sub StrongTypedEventHandler (Of TSender, TEventArgs As System.EventArgs) (sender As PublisherA, eventArgs) '
Fondamentalement, les délégués sont invariants dans les versions VB.NET 2005 et antérieures. J'ai en fait pensé à cette idée il y a quelques années, mais l'incapacité de VB.NET à gérer cela m'a dérangé ... Mais je suis maintenant passé fermement à C #, et VB.NET peut maintenant le gérer, donc, eh bien, par conséquent ce post.
Edit: mise à jour n ° 3
Ok, j'utilise cela avec succès depuis un moment maintenant. C'est vraiment un bon système. J'ai décidé de nommer mon "StrongTypedEventHandler" comme "GenericEventHandler", défini comme suit:
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
En dehors de ce changement de nom, je l'ai implémenté exactement comme indiqué ci-dessus.
Il déclenche la règle FxCop CA1009, qui stipule:
"Par convention, les événements .NET ont deux paramètres qui spécifient l'expéditeur de l'événement et les données de l'événement. Les signatures du gestionnaire d'événements doivent suivre cette forme: void MyEventHandler (expéditeur de l'objet, EventArgs e). Le paramètre 'sender' est toujours de type System.Object, même s'il est possible d'employer un type plus spécifique. Le paramètre «e» est toujours de type System.EventArgs. Les événements qui ne fournissent pas de données d'événement doivent utiliser le type de délégué System.EventHandler. Les gestionnaires d'événements renvoient void pour pouvoir envoyer chaque événement à plusieurs méthodes cibles. Toute valeur renvoyée par une cible serait perdue après le premier appel. "
Bien sûr, nous savons tout cela et enfreignons les règles de toute façon. (Tous les gestionnaires d'événements peuvent utiliser le standard 'object Sender' dans leur signature s'ils le souhaitent dans tous les cas - il s'agit d'un changement sans rupture.)
Donc, l'utilisation de a SuppressMessageAttribute
fait l'affaire:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
J'espère que cette approche deviendra la norme à un moment donné dans le futur. Cela fonctionne vraiment très bien.
Merci pour tous vos avis les gars, je l'apprécie vraiment ...
Mike
oh hi this my hom work solve it plz :code dump:
EventHandler<,>
que GenericEventHandler<,>
. Il existe déjà un générique EventHandler<>
dans BCL qui s'appelle simplement EventHandler. Donc EventHandler est un nom plus courant et les délégués prennent en charge les surcharges de type