C# Language
evenemang
Sök…
Introduktion
En händelse är ett meddelande om att något har inträffat (som ett musklick) eller i vissa fall håller på att inträffa (till exempel en prisändring).
Klasser kan definiera händelser och deras instanser (objekt) kan höja dessa händelser. Till exempel kan en knapp innehålla en klickhändelse som höjs när en användare har klickat på den.
Eventhanterare är sedan metoder som blir uppringda när deras motsvarande händelse tas upp. Ett formulär kan innehålla en klickad händelsehanterare för varje knapp som den till exempel innehåller.
parametrar
Parameter | detaljer |
---|---|
EventArgsT | Den typ som härrör från EventArgs och innehåller händelseparametrarna. |
Event namn | Namnet på händelsen. |
handler | Namnet på händelseshanteraren. |
SenderObject | Objektet som åberopar händelsen. |
EventArguments | En instans av typen EventArgsT som innehåller händelseparametrarna. |
Anmärkningar
När du höjer en händelse:
- Kontrollera alltid om delegaten är
null
. En null delegat betyder att evenemanget inte har några prenumeranter. Att höja en händelse utan prenumeranter kommer att resultera i enNullReferenceException
.
- Kopiera delegaten (t.ex.
EventName
) till en lokal variabel (t.ex.eventName
) innan du kontrollerar om null / höjer händelsen. Detta undviker rasförhållanden i flertrådiga miljöer:
Fel :
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.
Höger :
// 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);
- Använd operatören null-villkorad (?.) För att höja metoden i stället för att null-kontrollera delegaten för prenumeranter i ett
if
uttalande:EventName?.Invoke(SenderObject, new EventArgsT());
- När du använder Åtgärd <> för att deklarera delegattyper måste den anonyma metodens / händelseshanterarens signatur vara densamma som den deklarerade anonyma delegattypen i händelsedeklarationen.
Förklara och höja händelser
Förklara en händelse
Du kan förklara en händelse i valfri class
eller struct
med följande syntax:
public class MyClass
{
// Declares the event for MyClass
public event EventHandler MyEvent;
// Raises the MyEvent event
public void RaiseEvent()
{
OnMyEvent();
}
}
Det finns en utvidgad syntax för att deklarera händelser, där du har en privat instans av händelsen och definierar en offentlig instans med add
och set
accessors. Syntaxen liknar C # -egenskaperna. I alla fall bör syntaxen som visas ovan föredras, eftersom kompilatorn avger kod för att säkerställa att flera trådar säkert kan lägga till och ta bort händelseshanterare till händelsen i din klass.
Lyfter upp evenemanget
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);
}
Observera att händelser endast kan tas upp av den deklarera typen. Kunder kan bara prenumerera / avsluta prenumerationen.
För C # -versioner före 6.0, där EventName?.Invoke
inte stöds, är det en bra praxis att tilldela händelsen till en tillfällig variabel före kallelse, som visas i exemplet, vilket säkerställer tråd-säkerhet i fall där flera trådar kör samma koda. Om du inte gör det kan det leda till att en NullReferenceException
kastas i vissa fall där flera trådar använder samma objektinstans. I C # 6.0 avger kompilatorn kod som liknar den som visas i kodexemplet för C # 6.
Standardhändelsedeklaration
Eventdeklaration:
public event EventHandler<EventArgsT> EventName;
Eventförklaring:
public void HandlerName(object sender, EventArgsT args) { /* Handler logic */ }
Prenumererar på evenemanget:
dynamiskt:
EventName += HandlerName;
Genom Designer:
- Klicka på händelser-knappen i kontrollens egenskaperfönster (Lightening bolt)
- Dubbelklicka på händelsens namn:
- Visual Studio genererar händelsekoden:
private void Form1_Load(object sender, EventArgs e)
{
}
Åkalla metoden:
EventName(SenderObject, EventArguments);
Anonym förklaring av evenemangshanterare
Eventdeklaration:
public event EventHandler<EventArgsType> EventName;
Eventdeklaration med lambda operator => och prenumererar på händelsen:
EventName += (obj, eventArgs) => { /* Handler logic */ };
Förklaring av händelseshanterare med hjälp av delegerad anonym metatsyntax:
EventName += delegate(object obj, EventArgsType eventArgs) { /* Handler Logic */ };
Förklaring och prenumeration på en händelsehanterare som inte använder händelsens parameter och så kan använda ovanstående syntax utan att behöva ange parametrar:
EventName += delegate { /* Handler Logic */ }
Påkalla händelsen:
EventName?.Invoke(SenderObject, EventArguments);
Icke-standardförklaring om händelser
Händelser kan vara av valfri delegatyp, inte bara EventHandler
och EventHandler<T>
. Till exempel:
//Declaring an event
public event Action<Param1Type, Param2Type, ...> EventName;
Detta används på samma sätt som vanliga EventHandler
händelser:
//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, ...);
Det är möjligt att deklarera flera händelser av samma typ i en enda sats, liknande med fält och lokala variabler (även om det ofta kan vara en dålig idé):
public event EventHandler Event1, Event2, Event3;
Detta förklarar tre separata händelser ( Event1
, Event2
och Event3
) alla av typen EventHandler
.
Obs: Även om vissa kompilatorer kan acceptera denna syntax i gränssnitt såväl som klasser, tillhandahåller C # -specifikationen (v5.0 §13.2.3) grammatik för gränssnitt som inte tillåter det, så att använda detta i gränssnitt kan vara opålitligt med olika kompilatorer.
Skapa anpassade EventArgs som innehåller ytterligare data
Anpassade händelser behöver vanligtvis anpassade händelsesargument som innehåller information om händelsen. Till exempel MouseEventArgs
som används av MouseDown
som MouseDown
eller MouseUp
händelser, innehåller information om Location
eller Buttons
som användes för att generera händelsen.
När du skapar nya händelser, skapar du en anpassad händelse arg:
- Skapa en klass härrörande från
EventArgs
och definiera egenskaper för nödvändig data. - Som en konvention bör klassen namn slutas med
EventArgs
.
Exempel
I exemplet nedan skapar vi en PriceChangingEventArgs
händelse för egenskapen Price
i en klass. Händelsedataklassen innehåller en CurrentPrice
och en NewPrice
. Händelsen uppstår när du tilldelar ett nytt värde till fastigheten Price
och låter konsumenten veta att värdet förändras och låt dem veta om aktuellt pris och nytt pris:
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);
}
}
Du kan förbättra exemplet genom att låta konsumenten ändra det nya värdet och sedan kommer värdet att användas för egendom. För att göra det räcker det att tillämpa dessa ändringar i klasser.
Ändra definitionen av NewPrice
att den kan ställas in:
public int NewPrice { get; set; }
Ändra definitionen av Price
att använda e.NewPrice
som värde på egendom, efter att du har ringt OnPriceChanging
:
int price;
public int Price
{
get { return price; }
set
{
var e = new PriceChangingEventArgs(price, value);
OnPriceChanging(e);
price = e.NewPrice;
}
}
Skapa avbokningsbar händelse
En avbokningsbar händelse kan tas upp av en klass när den håller på att utföra en åtgärd som kan avbrytas, till exempel FormClosing
av ett Form
.
Så här skapar du en sådan händelse:
- Skapa en ny händelse som härrör från
CancelEventArgs
och lägg till ytterligare egenskaper för händelsedata. - Skapa en händelse med
EventHandler<T>
och använd den nya annulleringshändelsen arg-klassen som du skapade.
Exempel
I exemplet nedan skapar vi en PriceChangingEventArgs
händelse för egenskapen Price
i en klass. Händelsedataklassen innehåller ett Value
som låter konsumenten veta om det nya. Händelsen höjs när du tilldelar ett nytt värde till fastigheten Price
och låter konsumenten veta att värdet förändras och låter dem avbryta händelsen. Om konsumenten avbryter händelsen kommer det tidigare värdet för Price
att användas:
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);
}
}
Eventegenskaper
Om en klass höjer ett stort antal evenemang kanske lagringskostnaderna för ett fält per delegat inte är acceptabla. .NET Framework tillhandahåller händelseegenskaper för dessa fall. På så sätt kan du använda en annan datastruktur som EventHandlerList
att lagra evenemangsdelegater:
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);
}
}
Denna metod används ofta i GUI-ramar som WinForms där kontroller kan ha dussintals och till och med hundratals evenemang.
Observera att EventHandlerList
inte är trådsäker, så om du förväntar dig att din klass ska användas från flera trådar, måste du lägga till låsuttalanden eller annan synkroniseringsmekanism (eller använda en lagring som ger gängsäkerhet).