C# Language
Veranstaltungen
Suche…
Einführung
Ein Ereignis ist eine Benachrichtigung, dass ein Ereignis aufgetreten ist (z. B. ein Mausklick) oder in einigen Fällen unmittelbar bevorsteht (z. B. Preisänderung).
Klassen können Ereignisse definieren und ihre Instanzen (Objekte) können diese Ereignisse auslösen. Eine Schaltfläche kann beispielsweise ein Click-Ereignis enthalten, das ausgelöst wird, wenn ein Benutzer darauf geklickt hat.
Event-Handler sind dann Methoden, die aufgerufen werden, wenn das entsprechende Ereignis ausgelöst wird. Ein Formular kann beispielsweise einen Clicked-Ereignishandler für jede Schaltfläche enthalten, die es enthält.
Parameter
Parameter | Einzelheiten |
---|---|
EventArgsT | Der von EventArgs abgeleitete Typ, der die Ereignisparameter enthält. |
Veranstaltungsname | Der Name der Veranstaltung |
HandlerName | Der Name des Event-Handlers. |
SenderObject | Das Objekt, das das Ereignis aufruft. |
EventArguments | Eine Instanz des EventArgsT-Typs, die die Ereignisparameter enthält. |
Bemerkungen
Wenn Sie eine Veranstaltung auslösen:
- Überprüfen Sie immer, ob der Delegat
null
. Ein null-Delegat bedeutet, dass das Ereignis keine Abonnenten hat. Das Auslösen eines Ereignisses ohne Abonnenten führt zu einerNullReferenceException
.
- Kopieren Sie den Delegaten (z. B.
EventName
) in eine lokale Variable (z. B.eventName
), bevor Sie auf null überprüfen / das EreigniseventName
. Dadurch werden Race-Bedingungen in Umgebungen mit mehreren Threads vermieden:
Falsch :
if(Changed != null) // Changed has 1 subscriber at this point
// In another thread, that one subscriber decided to unsubscribe
Changed(this, args); // `Changed` is now null, `NullReferenceException` is thrown.
Richtig :
// Cache the "Changed" event as a local. If it is not null, then use
// the LOCAL variable (handler) to raise the event, NOT the event itself.
var handler = Changed;
if(handler != null)
handler(this, args);
- Verwenden Sie den nullbedingten Operator (?.),
EventName?.Invoke(SenderObject, new EventArgsT());
anstatt den Delegaten in einerif
EventName?.Invoke(SenderObject, new EventArgsT());
null zu überprüfen:EventName?.Invoke(SenderObject, new EventArgsT());
- Bei der Verwendung von Aktion <> zum Deklarieren von Delegattypen muss die anonyme Methoden- / Ereignishandler-Signatur mit dem deklarierten anonymen Delegattyp in der Ereignisdeklaration übereinstimmen.
Ereignisse deklarieren und anheben
Ein Ereignis deklarieren
Sie können ein Ereignis für jede class
oder struct
mit der folgenden Syntax deklarieren:
public class MyClass
{
// Declares the event for MyClass
public event EventHandler MyEvent;
// Raises the MyEvent event
public void RaiseEvent()
{
OnMyEvent();
}
}
Es gibt eine erweiterte Syntax für die Deklaration von Ereignissen, bei der Sie eine private Instanz des Ereignisses halten und eine öffentliche Instanz mithilfe von add
und set
Accessoren definieren. Die Syntax ist den C # -Eigenschaften sehr ähnlich. In allen Fällen sollte die oben dargestellte Syntax bevorzugt werden, da der Compiler Code ausgibt, um sicherzustellen, dass mehrere Threads Ereignishandler sicher zu dem Ereignis in Ihrer Klasse hinzufügen und entfernen können.
Das Ereignis auslösen
private void OnMyEvent()
{
EventName?.Invoke(this, EventArgs.Empty);
}
private void OnMyEvent()
{
// Use a local for EventName, because another thread can modify the
// public EventName between when we check it for null, and when we
// raise the event.
var eventName = EventName;
// If eventName == null, then it means there are no event-subscribers,
// and therefore, we cannot raise the event.
if(eventName != null)
eventName(this, EventArgs.Empty);
}
Beachten Sie, dass Ereignisse nur vom deklarierenden Typ ausgelöst werden können. Kunden können nur abonnieren / abbestellen.
Für C # -Versionen vor 6.0, bei denen EventName?.Invoke
nicht unterstützt wird, EventName?.Invoke
es sich, das Ereignis vor dem Aufruf einer temporären Variablen zuzuweisen ( EventName?.Invoke
gewährleistet die Thread-Sicherheit in Fällen, in denen mehrere Threads das gleiche ausführen Code. Andernfalls kann in bestimmten Fällen, in denen mehrere Threads dieselbe Objektinstanz verwenden, eine NullReferenceException
ausgelöst werden. In C # 6.0 gibt der Compiler Code aus, der dem im Codebeispiel für C # 6 gezeigten ähnlich ist.
Standard-Ereigniserklärung
Ereigniserklärung:
public event EventHandler<EventArgsT> EventName;
Event-Handler-Deklaration:
public void HandlerName(object sender, EventArgsT args) { /* Handler logic */ }
Anmeldung zum Event:
Dynamisch:
EventName += HandlerName;
Durch den Designer:
- Klicken Sie im Eigenschaftenfenster des Steuerelements auf die Schaltfläche Ereignisse (Blitz).
- Doppelklicken Sie auf den Ereignisnamen:
- Visual Studio generiert den Ereigniscode:
private void Form1_Load(object sender, EventArgs e)
{
}
Aufrufen der Methode:
EventName(SenderObject, EventArguments);
Erklärung zum anonymen Ereignishandler
Ereigniserklärung:
public event EventHandler<EventArgsType> EventName;
Event-Handler-Deklaration mit Lambda-Operator => und Abonnieren des Events:
EventName += (obj, eventArgs) => { /* Handler logic */ };
Event-Handler-Deklaration unter Verwendung der anonymen Methodenmethode für Delegaten :
EventName += delegate(object obj, EventArgsType eventArgs) { /* Handler Logic */ };
Deklaration und Subskription eines Ereignishandlers, der den Parameter des Ereignisses nicht verwendet und daher die obige Syntax verwenden kann, ohne Parameter angeben zu müssen:
EventName += delegate { /* Handler Logic */ }
Aufrufen der Veranstaltung:
EventName?.Invoke(SenderObject, EventArguments);
Nicht-Standard-Ereigniserklärung
Ereignisse können einen beliebigen EventHandler
, nicht nur EventHandler
und EventHandler<T>
. Zum Beispiel:
//Declaring an event
public event Action<Param1Type, Param2Type, ...> EventName;
Dies wird ähnlich wie bei Standard- EventHandler
Ereignissen verwendet:
//Adding a named event handler
public void HandlerName(Param1Type parameter1, Param2Type parameter2, ...) {
/* Handler logic */
}
EventName += HandlerName;
//Adding an anonymous event handler
EventName += (parameter1, parameter2, ...) => { /* Handler Logic */ };
//Invoking the event
EventName(parameter1, parameter2, ...);
Es ist möglich, mehrere Ereignisse desselben Typs in einer einzigen Anweisung zu deklarieren, ähnlich wie bei Feldern und lokalen Variablen (dies ist jedoch oft eine schlechte Idee):
public event EventHandler Event1, Event2, Event3;
Hiermit werden drei separate Ereignisse ( Event1
, Event2
und Event3
) vom Typ EventHandler
.
Anmerkung: Obwohl einige Compiler diese Syntax in Schnittstellen und Klassen akzeptieren, enthält die C # -Spezifikation (v5.0 §13.2.3) Grammatik für Schnittstellen, die dies nicht zulassen. Daher ist die Verwendung dieser Schnittstelle in Schnittstellen bei verschiedenen Compilern unzuverlässig.
Erstellen von benutzerdefinierten EventArgs mit zusätzlichen Daten
Für benutzerdefinierte Ereignisse sind normalerweise benutzerdefinierte Ereignisargumente erforderlich, die Informationen zum Ereignis enthalten. Beispielsweise enthält MouseEventArgs
das von Mausereignissen wie MouseDown
oder MouseUp
Ereignissen verwendet wird, Informationen zum Location
oder zu Buttons
denen das Ereignis generiert wurde.
Wenn Sie neue Ereignisse erstellen, erstellen Sie ein benutzerdefiniertes Ereignis arg:
- Erstellen Sie eine von
EventArgs
Klasse, und definieren Sie Eigenschaften für die erforderlichen Daten. - Als Konvention sollte der Name der Klasse mit
EventArgs
.
Beispiel
Im folgenden Beispiel erstellen wir ein PriceChangingEventArgs
Ereignis für die Price
Eigenschaft einer Klasse. Die Ereignisdatenklasse enthält einen CurrentPrice
und einen NewPrice
. Das Ereignis wird ausgelöst, wenn Sie der Eigenschaft Price
einen neuen Wert zuweisen und den Verbraucher wissen, dass sich der Wert ändert, und ihn über den aktuellen Preis und den neuen Preis informieren.
PriceChangingEventArgs
public class PriceChangingEventArgs : EventArgs
{
public PriceChangingEventArgs(int currentPrice, int newPrice)
{
this.CurrentPrice = currentPrice;
this.NewPrice = newPrice;
}
public int CurrentPrice { get; private set; }
public int NewPrice { get; private set; }
}
Produkt
public class Product
{
public event EventHandler<PriceChangingEventArgs> PriceChanging;
int price;
public int Price
{
get { return price; }
set
{
var e = new PriceChangingEventArgs(price, value);
OnPriceChanging(e);
price = value;
}
}
protected void OnPriceChanging(PriceChangingEventArgs e)
{
var handler = PriceChanging;
if (handler != null)
handler(this, e);
}
}
Sie können das Beispiel verbessern, indem Sie dem Verbraucher erlauben, den neuen Wert zu ändern. Der Wert wird dann für die Eigenschaft verwendet. Dazu genügt es, diese Änderungen in Klassen anzuwenden.
Ändern Sie die Definition von NewPrice
, dass sie einstellbar ist:
public int NewPrice { get; set; }
Ändern Sie die Definition von Price
, um e.NewPrice
als Eigenschaftswert zu verwenden, nachdem Sie OnPriceChanging
:
int price;
public int Price
{
get { return price; }
set
{
var e = new PriceChangingEventArgs(price, value);
OnPriceChanging(e);
price = e.NewPrice;
}
}
Stornierbares Ereignis erstellen
Ein stornierbares Ereignis kann von einer Klasse FormClosing
werden, wenn eine Aktion ausgeführt werden soll, die abgebrochen werden kann, beispielsweise das FormClosing
Ereignis eines Form
.
So erstellen Sie ein solches Ereignis:
- Erstellen Sie ein neues Ereignisargument, das von
CancelEventArgs
und fügen Sie zusätzliche Eigenschaften für Ereignisdaten hinzu. - Erstellen Sie ein Ereignis mit
EventHandler<T>
und verwenden Sie die neueEventHandler<T>
die Sie erstellt haben.
Beispiel
Im folgenden Beispiel erstellen wir ein PriceChangingEventArgs
Ereignis für die Price
Eigenschaft einer Klasse. Die Ereignisdatenklasse enthält einen Value
der den Verbraucher über das Neue informiert. Das Ereignis wird ausgelöst, wenn Sie der Eigenschaft Price
einen neuen Wert zuweisen. Der Verbraucher wird darüber informiert, dass sich der Wert ändert, und lässt das Ereignis abbrechen. Wenn der Verbraucher das Ereignis abbricht, wird der vorherige Wert für Price
verwendet:
PriceChangingEventArgs
public class PriceChangingEventArgs : CancelEventArgs
{
int value;
public int Value
{
get { return value; }
}
public PriceChangingEventArgs(int value)
{
this.value = value;
}
}
Produkt
public class Product
{
int price;
public int Price
{
get { return price; }
set
{
var e = new PriceChangingEventArgs(value);
OnPriceChanging(e);
if (!e.Cancel)
price = value;
}
}
public event EventHandler<PriceChangingEventArgs> PropertyChanging;
protected void OnPriceChanging(PriceChangingEventArgs e)
{
var handler = PropertyChanging;
if (handler != null)
PropertyChanging(this, e);
}
}
Ereignis-Eigenschaften
Wenn eine Klasse eine große Anzahl von Ereignissen auslöst, sind die Speicherkosten für ein Feld pro Delegat möglicherweise nicht akzeptabel. .NET Framework stellt Ereigniseigenschaften für diese Fälle bereit. Auf diese Weise können Sie eine andere Datenstruktur wie EventHandlerList
zum Speichern von Ereignisdelegaten verwenden:
public class SampleClass
{
// Define the delegate collection.
protected EventHandlerList eventDelegates = new EventHandlerList();
// Define a unique key for each event.
static readonly object someEventKey = new object();
// Define the SomeEvent event property.
public event EventHandler SomeEvent
{
add
{
// Add the input delegate to the collection.
eventDelegates.AddHandler(someEventKey, value);
}
remove
{
// Remove the input delegate from the collection.
eventDelegates.RemoveHandler(someEventKey, value);
}
}
// Raise the event with the delegate specified by someEventKey
protected void OnSomeEvent(EventArgs e)
{
var handler = (EventHandler)eventDelegates[someEventKey];
if (handler != null)
handler(this, e);
}
}
Dieser Ansatz ist in GUI-Frameworks wie WinForms weit verbreitet, wo Steuerelemente Dutzende und sogar Hunderte von Ereignissen enthalten können.
Beachten Sie, dass EventHandlerList
nicht threadsicher ist. Wenn Sie also erwarten, dass Ihre Klasse von mehreren Threads verwendet wird, müssen Sie EventHandlerList
oder einen anderen Synchronisationsmechanismus hinzufügen (oder einen Speicher verwenden, der Thread-Sicherheit bietet).