Si los eventos se implementan como delegados en .NET, ¿cuál es el punto de la sección .event IL?

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

Pregunta

He visto algunas muy buenas preguntas sobre el desbordamiento de pila en relación con los delegados, eventos y la implementación .NET de estas dos características. Una pregunta en particular, " ¿Cómo funcionan los eventos C # detrás de ¿escenas? " ;, produjo una gran respuesta que explica muy bien algunos puntos sutiles.

La respuesta a la pregunta anterior señala este punto:

  

Cuando declara un evento similar a un campo   ... el compilador genera los métodos   y un campo privado (del mismo tipo   como el delegado). Dentro de la clase,   cuando te refieres a ElementAddedEvent   Te estás refiriendo al campo. Fuera de   La clase, te refieres a la   campo

Un artículo de MSDN vinculado desde la misma pregunta (" Eventos similares a campos ") agrega:

  

La noción de elevar un evento es   precisamente equivalente a invocar el   delegado representado por el evento & # 8212;   Por lo tanto, no hay un lenguaje especial.   constructos para elevar eventos.

Queriendo examinar más a fondo, construí un proyecto de prueba para ver el IL al que se compilan un evento y un delegado para:

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

    public TestClass()
    { }
}

Esperaba que el handler del campo delegado y el evento FooEvent se compilaran aproximadamente en el mismo código IL, con algunos métodos adicionales para envolver el acceso al FooEvent campo. Pero el IL generado no fue exactamente lo que esperaba:

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

Como los eventos no son más que delegados con los métodos add y remove generados por el compilador, no esperaba ver los eventos tratados como algo más que eso en IL. Pero los métodos de agregar y quitar se definen en una sección que comienza con .event , no con .method como lo son los métodos normales.

Mis preguntas finales son: si los eventos se implementan simplemente como delegados con métodos de acceso, ¿cuál es el punto de tener una sección IL .event ? ¿No podrían implementarse en IL sin esto usando las secciones .method ? ¿Es .event equivalente a .method ?

¿Fue útil?

Solución

No estoy seguro de que sea sorprendente ... compare lo mismo para las propiedades vs campos (ya que las propiedades antes de la misma función que los eventos: encapsulación a través de los accesores):

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

También: los eventos no mencionan nada sobre los campos; solo definen los accesores (agregar / eliminar). El respaldo del delegado es un detalle de implementación; da la casualidad de que los campos-como-eventos declaran un campo como miembro de respaldo, de la misma manera que las propiedades implementadas automáticamente declaran un campo como miembro de respaldo. Otras implementaciones son posibles (y muy comunes, especialmente en formularios, etc.).

Otras implementaciones comunes:

Eventos dispersos (controles, etc.) - EventHandlerList (o similar):

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

(lo anterior es más o menos la columna vertebral de los eventos de formas de ganancia)

Fachada (aunque esto confunde al " remitente " un poco; a menudo es útil algún código intermediario):

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

(lo anterior también se puede usar para cambiar el nombre de un evento de manera efectiva)

Otros consejos

Los eventos no son lo mismo que los delegados. Los eventos encapsulan la adición / eliminación de un controlador para un evento. El manejador está representado con un delegado.

Usted podría simplemente escribir AddClickHandler / RemoveClickHandler, etc. para cada evento, pero sería relativamente doloroso y no facilitaría que las herramientas como VS separen los eventos de cualquier otra cosa.

Esto es como las propiedades realmente: puede escribir GetSize / SetSize, etc. (como lo hace en Java), pero al separar las propiedades, hay accesos directos sintácticos disponibles y un mejor soporte de herramientas.

El punto de tener eventos que son un par de métodos add, remove, es encapsulation .

La mayoría de las veces, los eventos se usan tal como están, pero otras veces no desea almacenar los delegados adjuntos al evento en un campo, o si desea realizar un procesamiento adicional al agregar o eliminar métodos de eventos.

Por ejemplo, una forma de implementar eventos de eficiencia de memoria es almacenar los delegados en un diccionario en lugar de un campo privado, porque los campos siempre se asignan mientras que un diccionario solo aumenta de tamaño cuando se agregan elementos. Este modelo es similar al que usan Winforms y WPF para hacer un uso eficiente de la memoria (winforms y WPF utilizan diccionarios con clave para almacenar delegados y no listas)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top