c# - tipos - Si los eventos se implementan como delegados en.NET, ¿cuál es el objetivo de la sección.event IL?
tipos de eventos en programacion (3)
He visto algunas preguntas muy buenas sobre Stack Overflow sobre 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 las escenas? ", Produjo una gran respuesta que explica muy bien algunos puntos sutiles.
La respuesta a la pregunta anterior hace este punto:
Cuando declara un evento parecido a un campo ... el compilador genera los métodos y un campo privado (del mismo tipo que el delegado). Dentro de la clase, cuando te refieres a ElementAddedEvent te refieres al campo. Fuera de la clase, te refieres al campo
Un artículo de MSDN vinculado desde la misma pregunta (" Eventos similares a campos ") agrega:
La noción de plantear un evento equivale precisamente a invocar al delegado representado por el evento, por lo tanto, no hay construcciones de lenguaje especiales para generar 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:
public class TestClass
{
public EventHandler handler;
public event EventHandler FooEvent;
public TestClass()
{ }
}
Esperaba que el handler
campo delegado y el evento FooEvent
compilaran aproximadamente el mismo código IL, con algunos métodos adicionales para ajustar el acceso al campo FooEvent
generado por el compilador. Pero el IL generado no era 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 métodos de 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 eliminar se definen en una sección que comienza .event
, not. .method
como lo hacen los métodos normales.
Mis preguntas principales son: si los eventos se implementan simplemente como delegados con métodos de acceso, ¿cuál es el sentido de tener una sección .event
IL? ¿No podrían implementarse en IL sin esto mediante el uso de secciones .method
? Es .event
equivalente a .method
?
El punto de tener eventos que son un par de métodos add, remove, es la encapsulación .
La mayoría de las veces los eventos se usan tal cual, pero otras veces no desea almacenar los delegados adjuntos al evento en un campo, o desea realizar un procesamiento adicional al agregar o quitar métodos de eventos.
Por ejemplo, una forma de implementar eventos de memoria eficiente es almacenar delegados en un diccionario en lugar de un campo privado, porque los campos siempre se asignan mientras que un diccionario solo crece en tamaño cuando se agregan elementos. Este modelo es similar con lo que usan Winforms y WPF para hacer un uso eficiente de la memoria (winforms y WPF usa diccionarios con clave para almacenar delegados, no listas)
Los eventos no son lo mismo que los delegados. Los eventos encapsulan agregar / eliminar un controlador para un evento. El controlador está representado con un delegado.
Simplemente podría escribir AddClickHandler / RemoveClickHandler, etc. para cada evento, pero sería relativamente doloroso y no facilitaría que herramientas como VS separen eventos de cualquier otra cosa.
Esto es exactamente como las propiedades en realidad: 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.
No estoy seguro de que sea sorprendente ... compare con el 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 descriptores de acceso):
.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)
}
Además, los eventos no mencionan nada sobre los campos; solo definen los accesadores (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 auto-implementadas declaran un campo como miembro de respaldo. Otras implementaciones son posibles (y muy comunes, especialmente en formularios, etc.).
Otras implementaciones comunes:
Sparse-events (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 bonito, mucho la columna vertebral de los eventos de win-forms)
Fachada (aunque esto confunde un poco al "emisor", a veces es útil un código intermedio):
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)