Si les événements sont implémentés en tant que délégués dans .NET, à quoi sert la section .event IL?

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

Question

J'ai vu de très bonnes questions sur le dépassement de capacité concernant les délégués, les événements et la mise en œuvre .NET de ces deux fonctionnalités. Une question en particulier, " Comment les événements C # fonctionnent-ils derrière le scènes? ", a produit une excellente réponse qui explique très bien certains points subtils.

La réponse à la question ci-dessus fait ressortir ce point:

  

Lorsque vous déclarez un événement semblable à un champ   ... le compilateur génère les méthodes   et un champ privé (du même type   en tant que délégué). Dans la classe,   lorsque vous vous référez à ElementAddedEvent   vous faites référence au terrain. À l'extérieur   la classe, vous faites référence à la   champ

Un article MSDN lié à la même question (& Événements de type champ. ") ajoute:

  

L’idée de faire un événement est   précisément équivalent à invoquer la   délégué représenté par l'événement & # 8212;   ainsi, il n'y a pas de langue spéciale   des constructions pour élever des événements

Désireux d’examiner plus en détail, j’ai construit un projet test afin de visualiser le livre final dans lequel un événement et un délégué sont compilés:

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

    public TestClass()
    { }
}

Je m'attendais à ce que le champ de délégué gestionnaire et l'événement FooEvent soient compilés en gros au même code IL, avec quelques méthodes supplémentaires pour encapsuler l'accès au code FooEvent . Mais l’IL généré ne correspondait pas exactement à ce que j’espérais:

.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
}

Comme les événements ne sont rien d'autre que des délégués avec les méthodes add et remove générées par le compilateur, je ne m'attendais pas à voir les événements traités autrement que dans IL. Mais les méthodes d'ajout et de suppression sont définies dans une section commençant par .event et non par .method comme le sont les méthodes normales.

Mes dernières questions sont les suivantes: si les événements sont implémentés simplement en tant que délégués avec des méthodes d'accès, à quoi sert-il d'avoir une section IL .event ? Ne pourraient-ils pas être implémentés dans IL sans cela en utilisant les sections .method ? Est-ce que .event est équivalent à .method ?

Était-ce utile?

La solution

Je ne suis pas sûr que ce soit surprenant ... comparez-le pour les propriétés vs les champs (puisque les propriétés ont la même fonction que les événements: encapsulation via des accesseurs):

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

De plus, les événements ne ne mentionnent rien à propos des champs; ils seulement définissent les accesseurs (ajout / suppression). Le délégué délégué est un détail de la mise en œuvre; il se trouve que des événements de type champ déclarent un champ en tant que membre de support - de la même manière que les propriétés implémentées automatiquement déclarent un champ en tant que membre de support. D'autres implémentations sont possibles (et très courantes, notamment dans les formulaires, etc.).

Autres implémentations courantes:

Evénements épars (contrôles, etc.) - EventHandlerList (ou similaire):

// 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);
}

(ce qui précède est à peu près la colonne vertébrale des événements win-forms)

Façade (bien que cela confonde un peu "l'expéditeur"; un code intermédiaire est souvent utile):

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

(ce qui précède peut également être utilisé pour renommer efficacement un événement)

Autres conseils

Les événements ne sont pas les mêmes que les délégués. Les événements encapsulent l'ajout / la suppression d'un gestionnaire pour un événement. Le gestionnaire est représenté par un délégué.

Vous pourriez écrire simplement AddClickHandler / RemoveClickHandler, etc. pour chaque événement - mais cela serait relativement pénible et il ne serait pas facile pour des outils comme VS de séparer les événements de tout autre événement.

C’est vraiment comme les propriétés - vous pouvez écrire GetSize / SetSize, etc. (comme vous le faites en Java), mais en séparant les propriétés, il existe des raccourcis syntaxiques disponibles et une meilleure prise en charge des outils.

Le point d’avoir des événements qui constituent une paire de méthodes d’ajouts / de suppressions est la encapsulation .

La plupart des événements temporels sont utilisés tels quels, mais d'autres fois, vous ne souhaitez pas stocker les délégués attachés à l'événement dans un champ, ou vous souhaitez effectuer un traitement supplémentaire lors de l'ajout ou de la suppression de méthodes d'événement.

Par exemple, un moyen d'implémenter les événements économes en mémoire est de stocker les délégués dans un dictionnaire plutôt que un champ privé, car les champs sont toujours alloués, alors que la taille d'un dictionnaire augmente uniquement lorsque des éléments sont ajoutés. Ce modèle est similaire à ce que Winforms et WPF utilisent utilisent efficacement la mémoire (Winforms et WPF utilisent des dictionnaires de clé pour stocker les délégués et non les listes)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top