Kapitel 24 Ereignisse
Eine Klasse kann ein Ereignis dazu verwenden, eine
andere Klasse (oder andere Klassen) über ein aufgetretenes Ereignis
zu benachrichtigen. Ereignisse verwenden das Veröffentlichen-Abonnieren-Prinzip;
eine Klasse veröffentlicht die Ereignisse, die sie ausgeben kann,
und Klassen, die Interesse an bestimmten Ereignissen haben, können
diese Ereignisse abonnieren.
Ereignisse werden in grafischen Benutzerschnittstellen
häufig dazu eingesetzt, über eine Benutzerauswahl zu informieren,
sie eignen sich jedoch auch für asynchrone Operationen, beispielsweise für Dateiänderungen oder
das Empfangen von E-Mail-Nachrichten.
Die Routine, mit der ein Ereignis aufgerufen wird,
wird durch eine Zuweisung (delegate) definiert. Zur einfacheren
Handhabung von Ereignissen legen die Entwurfskonventionen für Ereignisse
fest, dass die Zuweisung immer zwei Parameter umfasst. Der erste Parameter
bezeichnet das Objekt, durch das das Ereignis ausgelöst wurde,
der zweite Parameter ist ein Objekt, das Informationen zum Ereignis
enthält. Dieses Objekt leitet sich immer von der EventArgs-Klasse
ab.
24.1 Ein Ereignis zu einer neuen E-Mail-Nachricht
 
Hier ein Beispiel zu Ereignissen.
using System;
class NewEmailEventArgs: EventArgs
{
public NewEmailEventArgs(string subject, string message)
{
this.subject = subject;
this.message = message;
}
public string Subject
{
get
{
return(subject);
}
}
public string Message
{
get
{
return(message);
}
}
string subject;
string message;
}
class EmailNotify
{
public delegate void NewMailEventHandler(object sender,
NewEmailEventArgs e);
public event NewMailEventHandler OnNewMailHandler;
protected void OnNewMail(NewEmailEventArgs e)
{
if (OnNewMailHandler != null)
OnNewMailHandler(this, e);
}
public void NotifyMail(string subject, string message)
{
NewEmailEventArgs e = new NewEmailEventArgs(subject, message);
OnNewMail(e);
}
}
class MailWatch
{
public MailWatch(EmailNotify emailNotify)
{
this.emailNotify = emailNotify;
emailNotify.OnNewMailHandler +=
new EmailNotify.NewMailEventHandler(IHaveMail);
}
void IHaveMail(object sender, NewEmailEventArgs e)
{
Console.WriteLine("New Mail: {0}\n{1]",
e.Subject, e.Message);
}
EmailNotify emailNotify;
}
class Test
{
public static void Main()
{
EmailNotify emailNotify = new EmailNotify();
MailWatch mailWatch = new MailWatch(emailNotify);
emailNotify.NotifyMail("Hello!", "Welcome to Events!!!");
}
}
Die NewEMailEventArgs-Klasse enthält
die Informationen, die bei der Ausgabe eines NewEmail-Ereignisses
übergeben werden.
Die EMailNotify-Klasse ist für
die Ereignishandhabung verantwortlich; über diese Klasse wird die
Zuweisung deklariert, mit der definiert wird, welche Parameter bei Auftreten
des Ereignisses übergeben werden. Des Weiteren enthält die
Klasse eine Definition des Ereignisses selbst. Die OnNewMail()-Funktion
wird zur Ausgabe des Ereignisses eingesetzt, die Helferfunktion NotifyMail()
enthält die Ereignisinformationen, packt diese in eine Instanz
von NewEmailEventArgs und ruft OnNewMail()
zur Ereignisausgabe auf.
Die Klasse MailWatch ist ein Konsument
der EmailNotify-Klasse. Sie übernimmt eine Instanz
der EmailNotify-Klasse und koppelt die IHaveMail()-Funktion
an das OnNewMailHandler-Ereignis.
Abschließend erstellt die Main()-Funktion
Instanzen von EmailNotify und MailWatch und
ruft die Funktion NotifyMail() zur Ereignisausgabe auf.
24.2 Das Ereignisfeld
 
Im vorangegangenen Beispiel lautet das Ereignisfeld
EmailNotify.OnNewMailHandler. Innerhalb der Klasse, die
das Ereignisfeld enthält, gelten für dessen Verwendung keine
Einschränkungen.
Außerhalb der Deklaration von EmailNotify
jedoch kann ein Ereignisfeld nur für die linke Seite der Operatoren
+= und -= eingesetzt werden; das Feld kann
anderweitig weder untersucht noch geändert werden.
24.3 Multicastereignisse
 
C#-Ereignisse sind Multicastereignisse, d. h.,
beim Auslösen eines Ereignisses können mehrere Zuweisungen
mit den Ereignisinformationen aufgerufen werden. Die Aufrufreihenfolge
für Zuweisungen ist nicht definiert, und wenn eine Zuweisung eine
Ausnahme auslöst, kann dies dazu führen, dass andere Zuweisungen
nicht aufgerufen werden.
24.4 Selten verwendete Ereignisse
 
Die Mehrzahl der Klassen implementiert Ereignisse
über Ereignisfelder, wie im vorangegangenen Beispiel gezeigt wurde.
Werden durch eine Klasse zahlreiche Ereignisse implementiert, werden
von diesen Ereignissen voraussichtlich jedoch nur wenige gleichzeitig
eingesetzt (sparse events), stellt die Reservierung eines separaten Feldes
für jedes Ereignis eine Verschwendung von Speicherplatz dar. Dies
kann beispielsweise auf ein Benutzerschnittstellensteuerelement zutreffen, das viele Ereignisse unterstützt.
In dieser Situation kann eine Klasse anstelle von
Ereignisfeldern Ereigniseigenschaften deklarieren und einen privaten
Mechanismus zur Speicherung der zugrunde liegenden Zuweisungen verwenden.
Nachfolgend wurde das obige Beispiel überarbeitet, anstelle von
Ereignisfeldern werden Ereigniseigenschaften verwendet.
using System;
using System.Collections;
class NewEmailEventArgs: EventArgs
{
public NewEmailEventArgs(string subject, string message)
{
this.subject = subject;
this.message = message;
}
public string Subject
{
get
{
return(subject);
}
}
public string Message
{
get
{
return(message);
}
}
string subject;
string message;
}
class EmailNotify
{
public delegate void NewMailEventHandler(object sender,
NewEmailEventArgs e);
protected Delegate GetEventHandler(object key)
{
return((Delegate) handlers[key]);
}
protected void SetEventHandler(object key, Delegate del)
{
handlers.Add(key, del);
}
public event NewMailEventHandler OnNewMailHandler
{
get
{
return((NewMailEventHandler)
GetEventHandler(onNewMailKey));
}
set
{
SetEventHandler(onNewMailKey, value);
}
}
public void OnNewMail(NewEmailEventArgs e)
{
if (OnNewMailHandler != null)
OnNewMailHandler(this, e);
}
public void NotifyMail(string subject, string message)
{
NewEmailEventArgs e = new NewEmailEventArgs(subject, message);
OnNewMail(e);
}
Hashtable handlers = new Hashtable();
// Eindeutiger Schlüssel für dieses Ereignis
static readonly object onNewMailKey = new object();
}
class MailWatch
{
public MailWatch(EmailNotify emailNotify)
{
this.emailNotify = emailNotify;
emailNotify.OnNewMailHandler +=
new EmailNotify.NewMailEventHandler(IHaveMail);
}
void IHaveMail(object sender, NewEmailEventArgs e)
{
Console.WriteLine("New Mail: {0}\n{1]",
e.Subject, e.Message);
}
EmailNotify emailNotify;
}
class Test
{
public static void Main()
{
EmailNotify emailNotify = new EmailNotify();
MailWatch mailWatch = new MailWatch(emailNotify);
emailNotify.NotifyMail("Hello!", "Welcome to Events!!!");
}
}
Die EmailNotify-Klasse verfügt
nun anstelle eines gleichnamigen Ereignisses über eine Eigenschaft
namens NewMailEventHandler. Die Eigenschaft speichert die
Zuweisung in einer Hashtabelle (statt diese in einem Ereignisfeld zu platzieren)
und stellt mit dem statischen, schreibgeschützten Objekt onNewMailKey
sicher, dass die Eigenschaft die geeignete Zuweisung auffindet. Da garantiert
eindeutige Objektverweise über das System bereitgestellt werden,
kann durch das Erzeugen eines statischen, schreibgeschützten Objekts
zur Laufzeit ein eindeutiger Schlüssel generiert werden.
In dieser Situation wäre die Verwendung einer
Hashtabelle jedoch unangemessen, da mehr Speicherplatz als bei der vorherigen
Version belegt wird. Diese Vorgehensweise eignet sich eher für
Objekthierarchien – beispielsweise für Steuerelemente,
die von der Basisklasse Control abgeleitet werden –
mit vielen selten verwendeten Ereignissen (sparse events).
Die Control-Klasse implementiert GetEventHandler()
und SetEventHandler(). Alle Steuerelemente, die von der
Klasse abgeleitet werden, können hierüber die vorhandenen
Zuweisungen speichern.
Beachten Sie, dass dies nur ein Vorteil ist, wenn
voraussichtlich mehrere Instanzen jedes Steuerelements gleichzeitig
vorhanden sind. Ist dies nicht der Fall, überwiegt das Mehr an
Speicherplatz für den statischen, schreibgeschützten Schlüssel
gegenüber den Einsparungen im Objekt.
|