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 en NullReferenceException .
6,0
  • 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);
6,0
  • 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

6,0
private void OnMyEvent()
{
    EventName?.Invoke(this, EventArgs.Empty); 
}
6,0
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:

  1. Klicka på händelser-knappen i kontrollens egenskaperfönster (Lightening bolt)
  2. Dubbelklicka på händelsens namn:

ange bildbeskrivning här

  1. 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).



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow