Bei der Untersuchung von this question wurde ich neugierig, wie die neuen Kovarianz-/Kontravarianzfunktionen in C# 4.0 sich darauf auswirken.Kontravarianz von Ereignissen und Delegaten in .NET 4.0 und C# 4.0
In Beta 1 scheint C# mit der CLR nicht übereinzustimmen. Zurück in C# 3.0, wenn Sie hatte:
public event EventHandler<ClickEventArgs> Click;
... und dann an anderer Stelle Sie hatte:
button.Click += new EventHandler<EventArgs>(button_Click);
... der Compiler würde kotzen, weil sie unvereinbar Delegattypen sind. Aber in C# 4.0 kompiliert es gut, denn in CLR 4.0 ist der Typparameter jetzt als in
markiert, also ist es kontravariant, und so nimmt der Compiler an, dass der Multicastdelegate +=
funktioniert.
Hier ist mein 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");
}
}
Aber obwohl es kompiliert, es nicht zur Laufzeit arbeiten (ArgumentException
: Die Teilnehmer müssen vom gleichen Typ sein).
Es ist in Ordnung, wenn Sie nur einen der beiden Delegattypen hinzufügen. Aber die Kombination von zwei verschiedenen Typen in einem Multicast verursacht die Ausnahme, wenn der zweite hinzugefügt wird.
Ich denke, das ist ein Fehler in der CLR in Beta 1 (das Verhalten des Compilers sieht hoffentlich richtig aus).
Update für Release Candidate:
Der obige Code nicht mehr kompiliert. Es muss sein, dass die Kontravarianz von TEventArgs
im EventHandler<TEventArgs>
Delegattyp zurückgesetzt wurde, so dass dieser Delegat nun dieselbe Definition wie in .NET 3.5 hat.
Das heißt, die Beta-ich sah gehabt haben muss:
public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);
Jetzt ist es wieder da:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
Aber der Action<T>
Delegat Parameter T
ist noch kontra:
public delegate void Action<in T>(T obj);
Das gleiche gilt für 's T
ist kovariant.
Dieser Kompromiss ist sehr sinnvoll, solange wir davon ausgehen, dass die primäre Verwendung von Multicast-Delegaten im Kontext von Ereignissen liegt. Ich persönlich habe festgestellt, dass ich Multicast-Delegierte niemals als Ereignisse verwende.
Also ich denke, C# -Coding-Standards können jetzt eine neue Regel übernehmen: nicht Multicast-Delegaten von mehreren Delegaten Typen über Kovarianz/Kontravarianz bezogen bilden. Und wenn Sie nicht wissen, was das bedeutet, vermeiden Sie einfach die Verwendung von Action
für Ereignisse, um auf der sicheren Seite zu sein.
Natürlich hat diese Schlussfolgerung Auswirkungen auf the original question that this one grew from ...
Though interessant wollen die Frage? –
Ich bin überrascht, dass dieses Loch der Bekanntmachung des C# -Teams entgangen ist, sollte eines der ersten Dinge sein, die sie getestet hätten, nachdem sie die Varianz für generische Delegierte eingeführt haben, nicht wahr? C# 5 zeigt es auch (die clr-Version ist die gleiche). – nawfal