Se gli eventi sono implementati come delegati in .NET, qual è il punto della sezione IL .event?

StackOverflow https://stackoverflow.com/questions/219815

Domanda

Ho visto alcune ottime domande su Stack Overflow riguardanti delegati, eventi e l'implementazione .NET di queste due funzionalità. Una domanda in particolare, " Come funzionano gli eventi C # dietro scene? " ;, ha prodotto un'ottima risposta che spiega molto bene alcuni punti sottili.

La risposta alla domanda precedente sottolinea questo punto:

  

Quando dichiari un evento simile a un campo   ... il compilatore genera i metodi   e un campo privato (dello stesso tipo   come delegato). All'interno della classe,   quando ti riferisci a ElementAddedEvent   ti riferisci al campo. Al di fuori   la classe, ti riferisci al   campo

Un articolo MSDN collegato dalla stessa domanda (" Eventi simili a campi ") aggiunge:

  

L'idea di organizzare un evento è   esattamente equivalente a invocare il   delegato rappresentato dall'evento & # 8212;   quindi, non esiste un linguaggio speciale   costrutti per la raccolta di eventi.

Volendo esaminare ulteriormente, ho creato un progetto di test al fine di visualizzare l'IL che un evento e un delegato sono compilati per:

public class TestClass
{
    public EventHandler handler;
    public event EventHandler FooEvent;

    public TestClass()
    { }
}

Mi aspettavo che il campo delegato gestore e l'evento FooEvent si compilassero approssimativamente allo stesso codice IL, con alcuni metodi aggiuntivi per concludere l'accesso al Campo FooEvent . Ma l'IL generato non era esattamente quello che mi aspettavo:

.class public auto ansi beforefieldinit TestClass
    extends [mscorlib]System.Object
{
    .event [mscorlib]System.EventHandler FooEvent
    {
        .addon instance void TestClass::add_FooEvent(class [mscorlib]System.EventHandler)
        .removeon instance void TestClass::remove_FooEvent(class [mscorlib]System.EventHandler)
    }

    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        // Constructor IL hidden
    }

    .field private class [mscorlib]System.EventHandler FooEvent
    .field public class [mscorlib]System.EventHandler handler
}

Poiché gli eventi non sono altro che delegati con i metodi add e remove generati dal compilatore, non mi aspettavo di vedere gli eventi trattati come qualcosa di più di quello in IL. Ma i metodi di aggiunta e rimozione sono definiti in una sezione che inizia .event , non .method come sono i metodi normali.

Le mie ultime domande sono: se gli eventi vengono implementati semplicemente come delegati con metodi di accesso, che senso ha avere una sezione IL .event ? Non potrebbero essere implementati in IL senza questo usando le sezioni .method ? .event è equivalente a .method ?

È stato utile?

Soluzione

Non sono sicuro che sia sorprendente ... confronta lo stesso per proprietà vs campi (dal momento che le proprietà prima della stessa funzione degli eventi: incapsulamento tramite accessori):

.field public string Foo // public field
.property instance string Bar // public property
{
    .get instance string MyType::get_Bar()
    .set instance void MyType::set_Bar(string)
}

Inoltre - gli eventi non non menzionano nulla sui campi; solo definiscono gli accessori (aggiungi / rimuovi). Il backer delegato è un dettaglio di implementazione; accade che gli eventi field-like dichiarino un campo come membro di supporto, allo stesso modo in cui le proprietà implementate automaticamente dichiarano un campo come membro di supporto. Altre implementazioni sono possibili (e molto comuni, specialmente nelle forme ecc.).

Altre implementazioni comuni:

Sparse-events (Controlli, ecc.) - EventHandlerList (o simile):

// only one instance field no matter how many events;
// very useful if we expect most events to be unsubscribed
private EventHandlerList events = new EventHandlerList();
protected EventHandlerList Events {
    get { return events; } // usually lazy
}

// this code repeated per event
private static readonly object FooEvent = new object();
public event EventHandler Foo
{
    add { Events.AddHandler(FooEvent, value); }
    remove { Events.RemoveHandler(FooEvent, value); }
}
protected virtual void OnFoo()
{
    EventHandler handler = Events[FooEvent] as EventHandler;
    if (handler != null) handler(this, EventArgs.Empty);
}

(quanto sopra è praticamente la spina dorsale degli eventi Win-Form)

Facciata (anche se questo confonde un po 'il "mittente"; un codice intermedio è spesso utile):

private Bar wrappedObject; // via ctor
public event EventHandler SomeEvent
{
    add { wrappedObject.SomeOtherEvent += value; }
    remove { wrappedObject.SomeOtherEvent -= value; }
}

(quanto sopra può essere utilizzato anche per rinominare efficacemente un evento)

Altri suggerimenti

Gli eventi non sono gli stessi dei delegati. Gli eventi incapsulano l'aggiunta / rimozione di un gestore per un evento. Il gestore è rappresentato con un delegato.

potresti scrivere semplicemente AddClickHandler / RemoveClickHandler ecc per ogni evento - ma sarebbe relativamente doloroso e non renderebbe facile per strumenti come VS separare gli eventi da qualsiasi altra cosa.

Questo è proprio come le proprietà: potresti scrivere GetSize / SetSize ecc. (come fai in Java) ma separando le proprietà, ci sono scorciatoie sintattiche disponibili e un migliore supporto degli strumenti.

Il punto di avere eventi che sono una coppia di metodi add, remove, è incapsulamento .

La maggior parte delle volte gli eventi vengono utilizzati così come sono, ma altre volte non si desidera archiviare i delegati collegati all'evento in un campo o si desidera eseguire un'elaborazione aggiuntiva per aggiungere o rimuovere metodi di evento.

Ad esempio, un modo per implementare eventi efficienti in termini di memoria è archiviare i delegati in un dizionario piuttosto che un campo privato, poiché i campi vengono sempre allocati mentre un dizionario aumenta di dimensioni solo quando vengono aggiunti elementi. Questo modello è simile a quello che Winforms e WPF utilizzano fanno un uso efficiente della memoria (winforms e WPF utilizzano dizionari con chiave per memorizzare i delegati e non gli elenchi)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top