Event und Delegat Kontravarianz in .NET 4.0 und C# 4.0
Während der Untersuchung diese Frage ich habe neugierig, wie die neue Kovarianz/Kontravarianz features in C# 4.0 wird es betreffen.
In der Beta 1, C# scheint nicht einverstanden mit der CLR. Zurück in C# 3.0, wenn Sie hatte:
public event EventHandler<ClickEventArgs> Click;
... und dann anderswo, die Sie hatte:
button.Click += new EventHandler<EventArgs>(button_Click);
... würde der compiler barf, weil Sie nicht kompatibel sind Delegat-Typen. Aber in C# 4.0, kompiliert er fein, weil in CLR 4.0 der type-parameter ist nun markiert als in
, also es ist kontravariant, und so geht der compiler davon aus der multicast-Delegat +=
arbeiten.
Hier 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 auch wenn es kompiliert, es funktioniert nicht zur Laufzeit (ArgumentException
: die Delegierten müssen vom gleichen Typ sein).
Es ist okay, wenn Sie nur fügen Sie entweder eine der beiden Delegaten-Typen. Aber die Kombination von zwei verschiedene Arten in einer multicast die Ausnahme verursacht, wenn der zweite Hinzugefügt.
Ich denke, das ist ein Fehler in der CLR in der beta 1 (die compiler-Verhalten sieht hoffentlich richtige).
Update für den Release Candidate:
Den oben genannten code nicht mehr kompiliert. Es muss sein, dass die Kontravarianz von TEventArgs
im EventHandler<TEventArgs>
delegieren, geben Sie Rollback ausgeführt worden, so dass nun Delegierte hat die gleiche definition wie in .NET 3.5.
Ist, die beta die ich angeschaut gehabt haben muss:
public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);
Nun geht es zurück an:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
Aber die Action<T>
delegieren parameter T
noch kontravariant:
public delegate void Action<in T>(T obj);
Das gleiche gilt für Func<T>
's T
als kovariante.
Dieser Kompromiss macht sehr viel Sinn, solange wir davon ausgehen, dass die primäre Nutzung von multicast-Delegaten ist im Kontext der Ereignisse. Ich persönlich habe festgestellt, dass ich immer multicast-Delegaten, außer als Ereignisse.
Also ich denke mal C# coding standards können jetzt annehmen, eine neue Regel: keine form multicast-Delegaten, die von mehreren Delegierten Arten durch Kovarianz und Kontravarianz. Und wenn Sie nicht wissen, was das bedeutet, nur vermeiden Sie die Verwendung Action
für die Ereignisse auf der sicheren Seite.
Natürlich, die Schlussfolgerung hat Konsequenzen für die ursprüngliche Frage, dass dies ein Zuwachs von...
- Thoiugh interessant wollen ist die Frage?
- Ich bin überrascht, dass dieses Loch entkommen die Ankündigung von C# - team, sollte eines der ersten Dinge, die Sie getestet haben würde, nach der Einführung Varianz generischer delegates, nicht wahr? C# 5 zeigen auch Sie (die clr-version, in der die gleichen).
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sehr interessant. Sie nicht brauchen, um Ereignisse zu sehen, dass dies geschieht, und in der Tat finde ich es einfacher zu bedienen, einfache Delegierte.
Betrachten
Func<string>
undFunc<object>
. In C# 4.0 können Sie implizit konvertierenFunc<string>
zuFunc<object>
da kann man immer einen string als Referenz eine Referenz auf ein Objekt. Aber die Dinge schief gehen, wenn Sie versuchen, Sie zu kombinieren. Hier ist eine kurze, aber vollständige Programm demonstriert das problem auf zwei verschiedene Arten:Diese kompiliert in Ordnung, aber sowohl der
Combine
Anrufe (verdeckt durch das += syntaktischer Zucker) exceptions werfen. (Kommentieren Sie die ersten, um zu sehen, die zweite.)Dies ist definitiv ein problem, obwohl ich bin mir nicht genau sicher, was die Lösung sein soll. Es ist möglich, dass bei der Ausführung der Delegierte code müssen herausfinden, die am besten geeignete Art zu verwenden, basierend auf der Delegat-Typen beteiligt. Das ist ein bisschen böse. Es wäre ganz nett, eine generische
Delegate.Combine
rufen, aber Sie konnte nicht wirklich Ausdrücken die relevanten Arten in einer sinnvollen Art und Weise.Eine Sache, erwähnenswert ist, dass die kovariante Konvertierung ist ein Verweis-conversion - in die oben
multi1
undstringFactory
auf das gleiche Objekt beziehen: es ist nicht das gleiche wie das schreiben(An diesem Punkt wird die folgende Zeile ausgeführt wird, ohne Ausnahme.) Bei der Ausführung der BCL wirklich nicht haben, um mit einem
Func<string>
und einFunc<object>
kombiniert; es hat keine anderen Informationen.Böse, und ich ernsthaft hoffe es wird behoben, in gewisser Weise. Ich werde alert Mads und Eric zu dieser Frage, so können wir etwas mehr darüber informiert Kommentar.
Musste ich nur fix in meiner Anwendung. Ich habe die folgenden:
Ich weiß nicht, ob es relevante Unterschiede zu regelmäßigen multi-cast-Ereignisse. So weit wie ich es benutzt habe, funktioniert es ...
Übrigens: Ich mochte nie die Ereignisse in C#. Ich verstehe nicht, warum es ein feature, wenn es nicht bieten keine Vorteile.
ConcurrentBag<T>
statt der Liste.Sind Sie immer ArgumentException von beiden? Wenn die exception wird geworfen, indem Sie einfach den neuen handler, dann würde ich denken, dass es ist rückwärts-kompatibel.
BTW, ich glaube, Sie haben Ihre Kommentare vermischt. In C# 3.0 dies:
button.Click += new EventHandler<EventArgs>(button_Click); //old
hätte nicht laufen. Das ist c#4.0