2010-05-14 19 views
1

Si j'ai une classe qui déclenche un événement, avec (par exemple) FrobbingEventArgs, suis-je autorisé à le gérer avec une méthode qui prend EventArgs?En C#, les arguments du gestionnaire d'événements sont-ils contravariants?

Voici quelques code:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Frobber frobber = new Frobber(); 
     frobber.Frobbing += FrobberOnFrobbing; 
     frobber.Frob(); 
    } 

    private static void FrobberOnFrobbing(object sender, 
     EventArgs e) 
    { 
     // Do something interesting. Note that the parameter is 'EventArgs'. 
    } 
} 

internal class Frobber 
{ 
    public event EventHandler<FrobbingEventArgs> Frobbing; 
    public event EventHandler<FrobbedEventArgs> Frobbed; 

    public void Frob() 
    { 
     OnFrobbing(); 

     // Frob. 

     OnFrobbed(); 
    } 

    private void OnFrobbing() 
    { 
     var handler = Frobbing; 
     if (handler != null) 
     handler(this, new FrobbingEventArgs()); 
    } 

    private void OnFrobbed() 
    { 
     var handler = Frobbed; 
     if (handler != null) 
     handler(this, new FrobbedEventArgs()); 
    } 
} 

internal class FrobbedEventArgs : EventArgs { } 
internal class FrobbingEventArgs : EventArgs { } 

La raison pour laquelle je demande est que semble ReSharper d'avoir un problème avec (ce qui ressemble à) l'équivalent en XAML, et je me demande si c'est un bogue dans ReSharper, ou une erreur dans ma compréhension de C#.

+1

Il est un bon exemple de frobnication :) –

Répondre

6

Peut-être covariant est pas le mot

Fermer. Le mot que vous recherchez est contravariant. La conversion d'un groupe de méthodes en un type de délégué est covariant dans le type de retour et contravariante dans les types de paramètres formels.

Voici mon article de blog sur le sujet:

http://blogs.msdn.com/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx

si j'ai une classe qui déclenche un événement, avec (par exemple) FrobbingEventArgs, suis-je le droit de le manipuler avec une méthode qui prend EventArgs?

Eh bien, vous l'avez essayé et cela a fonctionné, donc clairement oui. Pour la justification dans la spécification, voir la section intitulée utilement "Conversions de groupes de méthodes".

+0

Eric, merci pour la correction - changé "covariant" à "contravariant". Merci aussi pour la justification. –

+0

Cela ne pose aucun problème et fonctionne correctement car la conversion du groupe de méthodes de la deuxième ligne de code dans 'Main' est convertie exactement au même type générique construit. (Le type d'exécution est égal au type compiletime.) Cela fonctionne également avec .NET 2.0. Juste si quelqu'un vient ici chercher _ "contravariance" _, notez ce qui suit (.NET 4.0): Ne faites pas un événement comme 'événement public Action Frobbing;' car 'Action' 'est contravariant (' in'). Les utilisateurs peuvent alors ajouter une action ou similaire à l'événement. Et cela ** ne fonctionnera pas ** si d'autres abonnés utilisent un autre 'Action '. –

1

Oui, vous pouvez mais lorsque vous accédez au paramètre e dans le gestionnaire d'événements, vous ne pourrez accéder aux membres appartenant à la classe de base EventArgs que si vous l'avez converti en tant que type dérivé. Je pense que le mot est polymorphe plutôt que covariant et toutes les classes de C# sont pollymorphes, c'est une caractéristique du langage.

+0

Le fait que FrobbedEventArgs peut être casté en EventArgs est polymorphisme, mais je pense que le fait qu'un délégué de type EventHandler peut être ajouté à un événement EventHandler est covariance. – Niki

+0

@nikie En fait, si vous regardez [EventHandler ] (http://msdn.microsoft.com/fr-fr/library/db0etb8x.aspx), vous verrez que c'est ** invariant **, non contravariant , dans son paramètre générique.Il dit 'EventHandler ', pas 'EventHandler '. Dans ce cas, il n'est donc pas possible d'ajouter un EventHandler à un événement EventHandler comme vous le dites. C'est donc la contravariance de la conversion du groupe de méthodes qui crée un 'EventHandler ' même si la méthode cible statique a des exigences "plus petites" pour son argument. –

+0

@JeppeStigNielsen: Le comportement que vous notez est une conséquence fâcheuse de la décision de Microsoft de ne pas demander aux délégués de définir une méthode 'Combine', mais plutôt d'utiliser' Delegate.Combine'. Étant donné les interfaces IFoo, IBar et IBoth: IFoo, IBar, Si le type de délégué 'Action ' a défini sa propre méthode de combinaison, il serait possible pour 'Action .Combine' d'accepter une' Action 'et une' Action ' et céder une 'Action '. En l'état, cependant, 'Delegate.Combine' ne peut pas déterminer le type de combinaison possible entre' Action 'et' Action '. – supercat

-1

Étant donné que les événements ne peuvent être invoqués qu'à partir de la classe qui les a déclarés, votre classe dérivée ne peut pas invoquer directement les événements déclarés dans la classe de base.

Vous pouvez réaliser ce que vous voulez en créant une méthode d'invocation protégée pour l'événement. En appelant cette méthode invoquant, votre classe dérivée peut appeler l'événement.

Pour encore plus de flexibilité, la méthode d'appel est souvent déclarée virtuelle, ce qui permet à la classe dérivée de la surcharger. Cela permet à la classe dérivée d'intercepter les événements que la classe de base appelle, en effectuant éventuellement son propre traitement.

Vous pouvez faire:

protected void OnFrobbing(EventArgs e) 
    { 
     var handler = Frobbing; 
     if (handler != null) 
     handler(this, new e); 
    } 

Ou:

protected virtual void OnFrobbing(EventArgs e) 
    { 
     var handler = Frobbing; 
     if (handler != null) 
     handler(this, new e); 
    } 
+0

-1: C'est le EventArgs qui est dérivé, pas la classe soulevant l'événement ... –