En examinant this question, j'ai été curieux de savoir comment les nouvelles caractéristiques de covariance/contravariance dans C# 4.0 l'affecteront.La contravariance des événements et des délégués dans .NET 4.0 et C# 4.0
Dans la version bêta 1, C# semble être en désaccord avec le CLR. Back in C# 3.0, si vous aviez:
public event EventHandler<ClickEventArgs> Click;
... et ailleurs, vous aviez:
button.Click += new EventHandler<EventArgs>(button_Click);
... le compilateur barf parce qu'ils sont les types de délégués incompatibles. Mais dans C# 4.0, il compile bien, car dans CLR 4.0 le paramètre de type est maintenant marqué comme in
, donc il est contravariant, et donc le compilateur suppose que le délégué de multidiffusion +=
fonctionnera.
Voici mon test:
public class ClickEventArgs : EventArgs { }
public class Button
{
public event EventHandler<ClickEventArgs> Click;
public void MouseDown()
{
Click(this, new ClickEventArgs());
}
}
class Program
{
static void Main(string[] args)
{
Button button = new Button();
button.Click += new EventHandler<ClickEventArgs>(button_Click);
button.Click += new EventHandler<EventArgs>(button_Click);
button.MouseDown();
}
static void button_Click(object s, EventArgs e)
{
Console.WriteLine("Button was clicked");
}
}
Mais bien qu'il compile, il ne fonctionne pas lors de l'exécution (ArgumentException
: Les délégués doivent être du même type).
Ce n'est pas grave si vous ajoutez uniquement l'un des deux types de délégué. Mais la combinaison de deux types différents dans une multidiffusion provoque l'exception lorsque la seconde est ajoutée.
Je suppose qu'il s'agit d'un bogue dans le CLR de la version bêta 1 (le comportement du compilateur semble idéal).
Mise à jour pour Release Candidate:
Le code ci-dessus ne compile. Il doit être que la contravariance de TEventArgs
dans le type de délégué EventHandler<TEventArgs>
a été annulée, alors que ce délégué a la même définition que dans .NET 3.5.
C'est, la bêta je regardais doit avoir:
public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);
Maintenant il est de retour à:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
Mais le paramètre délégué Action<T>
T
est encore contravariant:
public delegate void Action<in T>(T obj);
La même chose vaut pour Func<T>
T
étant covariant.
Ce compromis a beaucoup de sens, à condition de supposer que l'utilisation principale des délégués de multidiffusion est dans le contexte des événements. J'ai personnellement constaté que je n'utilise jamais de délégués de multidiffusion sauf en tant qu'événements. Je suppose que les normes de codage C# peuvent désormais adopter une nouvelle règle: ne formez pas de délégués de multidiffusion à partir de plusieurs types de délégués liés par covariance/contravariance. Et si vous ne savez pas ce que cela signifie, évitez simplement d'utiliser Action
pour que les événements soient du bon côté.
Bien entendu, cette conclusion a des implications pour the original question that this one grew from ...
Thoiugh intéressant est la question? –
Je suis surpris que ce trou a échappé à l'attention de l'équipe C#, devrait être l'une des premières choses qu'ils auraient testé après l'introduction de la variance pour les délégués génériques n'est-ce pas? C# 5 l'exhibe aussi (la version clr étant la même). – nawfal