Was sind die Unterschiede zwischen Delegierten und einem Ereignis? Enthalten nicht beide Verweise auf Funktionen, die ausgeführt werden können?
Antworten
Zu viele Anzeigen?Zusätzlich zu den syntaktischen und operativen Eigenschaften gibt es auch einen semantischen Unterschied.
Delegierte sind konzeptionell Funktionsvorlagen, d. h. sie drücken einen Vertrag aus, den eine Funktion einhalten muss, um als vom "Typ" des Delegierten zu gelten.
Ereignisse stellen ... nun ja, Ereignisse dar. Sie sollen jemanden benachrichtigen, wenn etwas passiert, und ja, sie halten sich an eine Delegatendefinition, aber sie sind nicht dasselbe.
Selbst wenn sie genau dasselbe wären (syntaktisch und im AWL-Code), bleibt der semantische Unterschied bestehen. Im Allgemeinen ziehe ich es vor, zwei verschiedene Namen für zwei verschiedene Konzepte zu haben, selbst wenn sie auf dieselbe Weise implementiert werden (was nicht bedeutet, dass ich denselben Code zweimal haben möchte).
Hier ist ein weiterer guter Link zum Nachschlagen. http://csharpindepth.com/Articles/Chapter2/Events.aspx
Kurz gesagt, die Take away aus dem Artikel - Ereignisse sind Kapselung über Delegaten.
Zitat aus dem Artikel:
Angenommen, es gäbe keine Ereignisse als Konzept in C#/.NET. Wie würde eine andere Klasse ein Ereignis abonnieren? Drei Möglichkeiten:
Eine öffentliche Delegiertenvariable
Eine Delegatenvariable, die von einer Eigenschaft unterstützt wird
Eine delegierte Variable mit den Methoden AddXXXHandler und RemoveXXXHandler
Option 1 ist aus den üblichen Gründen, aus denen wir öffentliche Variablen verabscheuen, eindeutig schrecklich.
Option 2 ist etwas besser, erlaubt es aber den Abonnenten, sich gegenseitig zu überschreiben - es wäre nur allzu einfach, someInstance.MyEvent = eventHandler; zu schreiben, was alle vorhandenen Event-Handler ersetzen würde, anstatt einen neuen hinzuzufügen. Darüber hinaus müssen Sie immer noch die Eigenschaften schreiben.
Option 3 ist im Grunde das, was Ihnen Ereignisse bieten, aber mit einer garantierten Konvention (vom Compiler generiert und durch zusätzliche Flags in der AWL unterstützt) und einer "freien" Implementierung, wenn Sie mit der Semantik zufrieden sind, die Ihnen feldartige Ereignisse bieten. Das Abonnieren und Abbestellen von Ereignissen ist gekapselt, ohne dass ein beliebiger Zugriff auf die Liste der Ereignisbehandler möglich ist, und Sprachen können die Dinge einfacher machen, indem sie
Was für ein großes Missverständnis zwischen Ereignissen und Delegierten!!! Ein Delegierter spezifiziert einen TYP (wie z.B. einen class
oder ein interface
tut), während ein Ereignis nur eine Art von MEMBER ist (wie Felder, Eigenschaften usw.). Und genau wie jede andere Art von Mitglied hat auch ein Ereignis einen Typ. Im Falle eines Ereignisses muss der Typ des Ereignisses jedoch durch einen Delegaten angegeben werden. Sie können zum Beispiel KEIN Ereignis von einem Typ deklarieren, der durch eine Schnittstelle definiert ist.
Zusammenfassend können wir Folgendes feststellen Beobachtung: Der Typ eines Ereignisses MUSS durch einen Delegierten definiert werden. . Dies ist die wichtigste Beziehung zwischen einem Ereignis und einem Delegierten und wird im Abschnitt II.18 Definition von Ereignissen de ECMA-335 (CLI) Teilbereiche I bis VI :
Bei typischer Verwendung wird die TypeSpec (falls vorhanden) identifiziert einen Delegierten deren Signatur mit den Argumenten übereinstimmt, die der Feuermethode des Ereignisses übergeben wurden.
Allerdings, diese Tatsache bedeutet NICHT, dass ein Ereignis ein hinterlegtes Delegatenfeld verwendet . In Wahrheit kann ein Ereignis ein Stützfeld eines beliebigen anderen Datenstrukturtyps verwenden. Wenn Sie ein Ereignis explizit in C# implementieren, können Sie frei wählen, wie Sie die Ereignisbehandler (Beachten Sie, dass Ereignisbehandler sind Instanzen der Art des Ereignisses die ihrerseits zwingend eine Delegatentyp ---von der vorherigen Beobachtung ). Aber Sie können diese Ereignisbehandler (die Delegateninstanzen sind) in einer Datenstruktur wie einer List
oder eine Dictionary
oder in einem anderen Feld oder sogar in einem hinterlegten Delegiertenfeld. Vergessen Sie aber nicht, dass die Verwendung eines Delegatenfeldes NICHT zwingend erforderlich ist.
HINWEIS: Wenn Sie Zugang haben zu C# 5.0 entfesselt Um die Unterschiede zwischen den beiden zu verstehen, lesen Sie bitte den Abschnitt "Beschränkungen für die einfache Verwendung von Delegierten" in Kapitel 18 "Ereignisse".
Es hilft mir immer, ein einfaches, konkretes Beispiel zu haben. Hier ist also eines für die Gemeinschaft. Zuerst zeige ich, wie man Delegierte allein verwenden kann, um das zu tun, was Ereignisse für uns tun. Dann zeige ich, wie die gleiche Lösung mit einer Instanz von EventHandler
. Und dann erkläre ich, warum wir das, was ich im ersten Beispiel erkläre, NICHT tun sollten. Dieser Beitrag wurde inspiriert von ein Artikel von John Skeet.
Beispiel 1: Verwendung eines öffentlichen Delegaten
Angenommen, ich habe eine WinForms-Anwendung mit einem einzigen Dropdown-Feld. Das Dropdown-Feld ist gebunden an eine List<Person>
. Wobei Person die Eigenschaften Id, Name, NickName, HairColor hat. Auf dem Hauptformular befindet sich ein benutzerdefiniertes Steuerelement, das die Eigenschaften dieser Person anzeigt. Wenn jemand eine Person in der Dropdown-Liste auswählt, werden die Beschriftungen in der Benutzerkontrolle aktualisiert, um die Eigenschaften der ausgewählten Person anzuzeigen.
Das funktioniert folgendermaßen. Wir haben drei Dateien, die uns helfen, dies zusammenzustellen:
- Mediator.cs - statische Klasse enthält die Delegierten
- Form1.cs - Hauptformular
- DetailView.cs - Benutzersteuerung zeigt alle Details
Hier ist der entsprechende Code für jede der Klassen:
class Mediator
{
public delegate void PersonChangedDelegate(Person p); //delegate type definition
public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
{
if (PersonChangedDel != null)
{
PersonChangedDel(p);
}
}
}
Hier ist unsere Benutzerkontrolle:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.PersonChangedDel += DetailView_PersonChanged;
}
void DetailView_PersonChanged(Person p)
{
BindData(p);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Schließlich haben wir den folgenden Code in unserer Form1.cs. Hier rufen wir OnPersonChanged auf, das jeden Code aufruft, der den Delegaten abonniert hat.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}
Gut. So würden Sie das zum Laufen bringen ohne Verwendung von Ereignissen y nur mit Delegierten . Wir fügen einfach einen öffentlichen Delegaten in eine Klasse ein - Sie können ihn statisch machen oder ein Singleton oder was auch immer. Großartig.
ABER, ABER, ABER, wir wollen nicht das tun, was ich gerade oben beschrieben habe. Denn öffentliche Felder sind schlecht aus vielen, vielen Gründen. Was sind also unsere Optionen? Wie John Skeet beschreibt, gibt es folgende Möglichkeiten:
- Eine öffentliche Delegatenvariable (das ist das, was wir gerade oben getan haben. Tun Sie das nicht. Ich habe Ihnen gerade oben gesagt, warum das schlecht ist)
- Setzen Sie den Delegaten in eine Eigenschaft mit einem get/set (Problem hier ist, dass Abonnenten sich gegenseitig überschreiben könnten -- so könnten wir eine Reihe von Methoden auf den Delegaten abonnieren und dann könnten wir versehentlich sagen
PersonChangedDel = null
und löscht damit alle anderen Abonnements aus. Das andere Problem, das hier bleibt, ist, dass, da die Benutzer Zugriff auf den Delegaten haben, sie die Ziele in der Aufrufliste aufrufen können - wir wollen nicht, dass externe Benutzer Zugriff darauf haben, wann sie unsere Ereignisse auslösen. - Eine delegierte Variable mit den Methoden AddXXXHandler und RemoveXXXHandler
Diese dritte Option ist im Wesentlichen das, was uns ein Ereignis bietet. Wenn wir einen EventHandler deklarieren, gibt er uns Zugriff auf einen Delegaten - nicht öffentlich, nicht als Eigenschaft, sondern als dieses Ding, das wir ein Ereignis nennen, das nur Accessoren zum Hinzufügen/Entfernen hat.
Schauen wir uns an, wie das gleiche Programm aussieht, aber jetzt mit einem Ereignis anstelle des öffentlichen Delegaten (ich habe auch unseren Mediator in ein Singleton geändert):
Beispiel 2: Mit EventHandler anstelle eines öffentlichen Delegaten
Vermittler:
class Mediator
{
private static readonly Mediator _Instance = new Mediator();
private Mediator() { }
public static Mediator GetInstance()
{
return _Instance;
}
public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.
public void OnPersonChanged(object sender, Person p)
{
var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
if (personChangedDelegate != null)
{
personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
}
}
}
Wenn Sie mit F12 auf den EventHandler klicken, wird Ihnen angezeigt, dass es sich bei der Definition nur um einen generischen Delegaten mit dem zusätzlichen Objekt "Absender" handelt:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
Die Benutzerkontrolle:
public partial class DetailView : UserControl
{
public DetailView()
{
InitializeComponent();
Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
}
void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
{
BindData(e.Person);
}
public void BindData(Person p)
{
lblPersonHairColor.Text = p.HairColor;
lblPersonId.Text = p.IdPerson.ToString();
lblPersonName.Text = p.Name;
lblPersonNickName.Text = p.NickName;
}
}
Zum Schluss noch der Code von Form1.cs:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}
Da der EventHandler ein EventArgs als Parameter benötigt, habe ich diese Klasse mit nur einer einzigen Eigenschaft erstellt:
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
Ich hoffe, das zeigt Ihnen ein wenig, warum wir Ereignisse haben und wie sie sich von den Delegierten unterscheiden - aber funktionell gleich sind -.
- See previous answers
- Weitere Antworten anzeigen