Signature d'événement dans .NET — à l'Aide d'une Forte Tapé "Expéditeur"?

J'ai pleinement conscience que ce que je propose n'a pas suivi la .NET des lignes directrices, et, par conséquent, est probablement une mauvaise idée pour cette seule raison. Cependant, j'aimerais réfléchir à ce à partir de deux perspectives possibles:

(1) dois-je envisager d'utiliser cela pour mon propre travail de développement, qui est de 100% pour des fins internes.

(2) Est-ce un concept que le cadre designers pourrait envisager de modifier ou de mettre à jour?

Je pense à propos de l'utilisation d'un événement de signature qui utilise une forte tapé "expéditeur", au lieu de taper comme "objet", qui est le courant .NET design pattern. C'est, au lieu d'utiliser un événement de signature qui ressemble à ceci:

class Publisher
{
    public event EventHandler<PublisherEventArgs> SomeEvent;
}

Je suis envisage d'utiliser un événement de signature qui utilise un fort typée "expéditeur" du paramètre, comme suit:

D'abord, de définir 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 faisant usage de la StrongTypedEventHandler, nous imposons que la TEventArgs dérive de System.EventArgs.

Suivant, à titre d'exemple, nous pouvons faire usage de la StrongTypedEventHandler dans une publication de classe comme suit:

class Publisher
{
    public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;

    protected void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            SomeEvent(this, new PublisherEventArgs(...));
        }
    }
}

L'arrangement ci-dessus serait de permettre aux abonnés d'utiliser un fort typée gestionnaire d'événements qui ne nécessitent pas de casting:

class Subscriber
{
    void SomeEventHandler(Publisher sender, PublisherEventArgs e)
    {           
        if (sender.Name == "John Smith")
        {
            //...
        }
    }
}

Je n'ai pleinement conscience que cela rompt avec la norme .NET en cas de manipulation de modèle; cependant, gardez à l'esprit que la contravariance permettant à un abonné d'utiliser un événement traditionnel de la manipulation de la signature si vous le souhaitez:

class Subscriber
{
    void SomeEventHandler(object sender, PublisherEventArgs e)
    {           
        if (((Publisher)sender).Name == "John Smith")
        {
            //...
        }
    }
}

Qui est, si un gestionnaire d'événement nécessaire de s'abonner à des événements disparates (ou peut-être inconnu) types d'objet, le gestionnaire pourrait le type de l '"expéditeur" paramètre "objet" afin de gérer l'intégralité du potentiel de l'expéditeur objets.

Autres que la rupture de la convention (qui est quelque chose que je ne prends pas à la légère, croyez-moi) je ne peux pas penser à tout les inconvénients de cette.

Il peut y avoir quelques CLS de la conformité des problèmes ici. Ce n'est exécuté dans Visual Basic .NET 2008 100% de fines (j'ai testé), mais je crois que les versions antérieures de Visual Basic .NET par le biais de 2005 n'ont pas de délégué de la covariance et la contravariance. [Edit: depuis, j'ai testé, et c'est confirmé: VB.NET 2005 et ci-dessous ne peut pas gérer cela, mais VB.NET 2008 est de 100% de fines. Voir la section "Edit #2", ci-dessous.] Il y a peut être d'autres .NET les langues qui ont aussi un problème avec cela, je ne peux pas en être sûr.

Mais je ne me vois pas le développement de toute autre langue que le C# ou Visual Basic .NET, et je n'ai pas l'esprit de la restriction de C# et VB.NET pour .NET Framework 3.0 et au-dessus. (Je ne pouvais pas imaginer revenir à 2.0 à ce point, pour être honnête.)

Une autre personne peut penser à un problème avec cela? Ou est-ce tout simplement rompre avec la convention, si bien qu'il fait de l'estomac des gens tourner?

Voici quelques liens que j'ai trouvé:

(1) La Conception de l'événement lignes Directrices [MSDN 3.5]

(2) C# d'Événements simples Sensibiliser à l'aide de “expéditeur” vs personnalisé EventArgs [StackOverflow 2009]

(3) Signature d'événement motif dans .net [StackOverflow 2008]

Je suis intéressé par n'importe qui et tout le monde est d'avis sur cette...

Merci d'avance,

Mike

Edit #1: C'est en réponse à Tommy Carlier post :

Ici un exemple qui montre que les deux fort typée gestionnaires d'événements et la norme actuelle des gestionnaires d'événements que l'utilisation d'un objet expéditeur " paramètre peut co-exister avec cette approche. Vous pouvez copier-coller dans le code et de lui donner une course:

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: C'est en réponse à Andrew Hare, déclaration concernant la covariance et la contravariance et comment il s'applique ici. Les délégués dans le langage C# ont eu la covariance et la contravariance pendant si longtemps qu'elle se sent juste "intrinsèque", mais il ne l'est pas. Il pourrait même être quelque chose qui est activé dans le CLR, je ne sais pas, mais Visual Basic .NET n'a pas obtenu la covariance et la contravariance capacité de ses délégués, jusqu'à ce que l' .NET Framework 3.0 (VB.NET 2008). Et comme un résultat, Visual Basic.NET pour .NET 2.0 et au-dessous ne serait pas en mesure d'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 s'exécuter à 100% de fines. Mais j'ai maintenant testé sur VB.NET 2005, juste pour être sûr, et il ne compile pas, en déclarant:

Méthode " Public Sub
SomeEventHandler(sender as Object, e
Comme
vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)'
ne pas avoir la même signature que
délégué Délégué Sous
StrongTypedEventHandler(De TSender,
TEventArgs En Tant Que Système.EventArgs)(expéditeur
En tant qu'Éditeur, e
PublisherEventArgs)'

Fondamentalement, les délégués sont invariantes dans VB.NET les versions 2005 et ci-dessous. J'ai effectivement pensé à cette idée il y a quelques années, mais VB.NET s'incapacité à faire face à ce qui me dérangeait beaucoup... Mais j'ai maintenant déménagé solidement à C#, et VB.NET peut maintenant en occuper, donc, également, d'où ce post.

Edit: Mise À Jour #3

Ok, j'ai été en utilisant ce assez de succès pour un certain temps maintenant. C'est vraiment un très bon système. J'ai décidé de nommer mon "StrongTypedEventHandler" comme "GenericEventHandler", définis comme suit:

[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;

Autre que ce changement de nom, j'ai mis en place exactement comme discuté ci-dessus.

Il ne voyage plus de FxCop règle CA1009, qui stipule:

"Par convention .NET événements ont deux
les paramètres qui spécifient l'événement
de l'expéditeur et les données de l'événement. Gestionnaire d'événements
les signatures doivent suivre ce formulaire:
void MyEventHandler( object sender,
EventArgs e). L '"expéditeur" paramètre
est toujours de type System.Objet, même
si il est possible d'employer un plus
type spécifique. Le 'e' le paramètre est
toujours de type System.EventArgs.
Les événements qui ne fournissent pas les données de l'événement
doit utiliser le Système.Gestionnaire d'événements
type de délégué. Les gestionnaires d'événements de retour
nul, de sorte qu'ils peuvent envoyer chaque événement
de multiples méthodes cible. Toute valeur
renvoyée par la cible serait perdu
après le premier appel."

Bien sûr, nous savons tous cela, et ne respectent pas les règles de toute façon. (Tous les gestionnaires d'événements peuvent utiliser la norme object Sender' dans leur signature si l'on préfère, en tout cas c'est un non-modification de rupture.)

Ainsi, l'utilisation d'un SuppressMessageAttribute fait le truc:

[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]

J'espère que cette approche devient la norme à un certain moment dans l'avenir. Il fonctionne vraiment très bien.

Merci à tous pour vos avis les gars, j'ai vraiment l'apprécier...

Mike

  • Le faire. (Ne pense pas que cela justifie une réponse.)
  • Mes arguments n'étaient pas vraiment pointé sur vous: bien sûr, vous devriez le faire dans vos propres projets. Ils ont été des arguments pourquoi il ne pourrait pas travailler dans la BCL.
  • Gotcha, merci Tommy. (Mais je pense qu'il serait de travailler largement, au moins avec tout .NET langue qui avait délégué la contravariance.)
  • L'homme, je souhaite que mon projet avait fait depuis le début, je déteste le casting de l'expéditeur.
  • Maintenant CE est une question. Voir, les gens? Pas un seul de ces tweet taille oh hi this my hom work solve it plz :code dump: questions, mais une question nous apprendre.
  • Une autre suggestion, juste de nom, il EventHandler<,> que GenericEventHandler<,>. Il y a déjà des génériques EventHandler<> dans la BCL qui est nommé simplement Gestionnaire d'événements. Donc, ce Gestionnaire est un nom plus commun et des délégués prend en charge le type de surcharges