35 votes

Bogues de covariance et de contravariance dans .NET 4.0

Un comportement étrange avec le support C# 4.0 co- et contravariance :

using System;

class Program {
  static void Foo(object x) { }
  static void Main() {
    Action<string> action = _ => { };

    // C# 3.5 supports static co- and contravariant method groups
    // conversions to delegates types, so this is perfectly legal:
    action += Foo;

    // since C# 4.0 much better supports co- and contravariance
    // for interfaces and delegates, this is should be legal too:
    action += new Action<object>(Foo);
  }
}

Ce sont les résultats avec ArgumentException: Delegates must be of the same type.

C'est étrange, n'est-ce pas ? Pourquoi Delegate.Combine() (qui est appelé lors de l'exécution de += sur les délégués) ne supporte pas la co- et la contravariance au moment de l'exécution ?

De plus, j'ai trouvé que le BCL System.EventHandler<TEventArgs> Le type de délégué n'a pas d'annotation contravariante sur son générique. TEventArgs paramètre ! Pourquoi ? C'est parfaitement légal, TEventArgs type utilisé uniquement à la position d'entrée. Peut-être qu'il n'y a pas d'annotation contravariante parce qu'elle cache joliment le bug avec l'annotation Delegate.Combine() ? ;)

p.s. Tout ceci concerne les versions VS2010 RC et ultérieures.

38voto

Eric Lippert Points 300275

En bref, la combinaison de délégués est tout est en désordre par rapport à la variance. Nous l'avons découvert tardivement dans le cycle. Nous travaillons avec l'équipe CLR pour voir si nous pouvons trouver un moyen de faire fonctionner tous les scénarios courants sans rompre la rétrocompatibilité, etc., mais ce que nous trouverons ne sera probablement pas intégré à la version 4.0. Nous espérons que tout cela sera réglé dans un Service Pack. Je m'excuse pour le désagrément.

6voto

Tomas Petricek Points 118959

La covariance et la contravariance spécifient la relation d'héritage entre les types génériques. Lorsque vous avez la covariance et la contravariance, les classes G<A> y G<B> peut être dans une certaine relation d'héritage en fonction de ce qui A y B est. Vous pouvez en bénéficier lorsque vous appelez des méthodes génériques.

Toutefois, le Delegate.Combine n'est pas générique et la méthode La documentation indique clairement quand l'exception sera levée :

ArgumentException - Les deux a y b ne sont pas null référence ( Nothing en Visual Basic), et a y b ne sont pas des instances du même type de délégué.

Ahora, Action<object> y Action<string> sont certainement des instances d'un type de délégué différent (même si elles sont liées par une relation d'héritage), donc selon la documentation, une exception sera levée. Il semble raisonnable que le Delegate.Combine pourrait prendre en charge ce scénario, mais ce n'est qu'une proposition possible (évidemment, cela n'était pas nécessaire jusqu'à présent, car vous ne pouvez pas déclarer de délégués hérités, donc avant la co/contra-variance, aucun délégué n'avait de relation d'héritage).

1voto

supercat Points 25534

Une difficulté avec la combinaison de délégués est que, à moins de spécifier quel opérande est censé être le sous-type et quel est le super-type, le type du résultat n'est pas clair. Il est possible d'écrire une fabrique de wrapper qui convertira n'importe quel délégué avec un nombre spécifié d'arguments et un modèle de byval/byref en un supertype, mais appeler une telle fabrique plusieurs fois avec le même délégué produirait des wrappers différents (cela pourrait avoir des conséquences sur la désinscription des événements). On pourrait aussi créer une version de Delegate.Combine qui contraindrait le délégué de droite au type du délégué de gauche (en bonus, le retour n'aurait pas à être typé), mais il faudrait écrire une version spéciale de delegate.remove pour s'en occuper.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X