delegate - C#- ¿Ventajas de la palabra clave del evento?
eventhandler<> (3)
¿Cuál es la ventaja de usar la palabra clave del evento que no sea para modificar cómo se puede acceder al delegado?
Esa es la principal ventaja de usar la palabra clave de evento. Se usa un evento solo sobre un delegado sin procesar para evitar que el delegado se invoque o elimine desde fuera del alcance de la clase en la que está definido, porque en el caso de los eventos es responsabilidad de esa clase invocar el evento. Las entidades externas no deberían invocarlo directamente (pueden y deben invocar el evento indirectamente), ni deberían "preocuparse" de si hay otros controladores de eventos o estar involucrados en tocarlos (por ejemplo, asignando un nuevo delegado al campo).
El caso específico de querer permitir que las subclases activen el evento se resuelve más comúnmente al tener la clase que define el evento creando un método protegido que no hace más que disparar el evento. Dichos métodos tendrán, por convención, el mismo nombre que el evento pero con "Encendido" prefiándolo.
Sí, podría crear su propio tipo que represente lógicamente un evento, sea un contenedor para un delegado y limite las funciones que se pueden realizar en ese evento a aquellas que "deberían" poder realizarlas (posiblemente usando reglas ligeramente diferentes de se usa la palabra clave del event
C #. Esto es algo que se usa con frecuencia en otros idiomas que no tienen una palabra clave de event
(o incluso delegados). Los diseñadores de C # simplemente se dieron cuenta de que este era un patrón muy común y creían que valía la pena la energía para agregar la palabra clave al idioma para ayudar a minimizar el código repetitivo requerido para crear un "evento" lógico.
Otro beneficio de utilizar la palabra clave de event
, en lugar de simplemente tener algún tipo de delegado como propiedad, es que usted hace sus intenciones mucho más claras. Si veo solo una propiedad de delegado, la implicación es, en general, que representa un método. Sí, todos los delegados en C # son delegados de multidifusión, por lo que no es cierto, pero es inusual que la gente aproveche esa funcionalidad fuera de los eventos. La gente piensa que una Action
representa una acción, no una lista de acciones. Los eventos también tienen un tratamiento especial con respecto a la documentación de C #. Todos se enumeran por separado, tienen diferentes iconos en el estudio visual, etc. Todo esto ayuda a que las intenciones y la semántica del miembro sean mucho más claras para alguien que usa la clase de un vistazo.
Finalmente, la palabra clave de event
asegura que hay sincronización entre múltiples hilos, que no es realizada por la clase Delegate
. Si hay varios subprocesos para agregar controladores a un evento al mismo tiempo, la palabra clave del event
garantiza que ambos se agreguen. Si solo expone públicamente a un delegado, es posible que uno sobrescriba al otro debido a una condición de carrera y que un controlador termine en el suelo. Si publicas tu propia clase de Event
, puedes proporcionar esta funcionalidad, pero es tanto un código más repetitivo como algo bastante fácil de perder (ya sea que se dejen condiciones de carrera o una sincronización excesiva que resulte en un rendimiento perdido).
Recientemente llegué a entender que realmente es un ''evento'' de C #. En realidad no es nada, sinceramente. Para resumir mis hallazgos: la palabra clave del evento es simplemente un modificador que solo se aplica a los delegados.
Entonces, toda la ''magia'' de un evento son las operaciones de un delegado. Eso es. He leído mucha de la documentación de Microsoft, pero no hay una frase que resuma de esa manera tan sucintamente. Para continuar con mis hallazgos, delegado, clase y estructura están todos en el mismo ''nivel''. Son formas de definir un ''objeto''. No me refiero a ''objeto'' como en el tipo, sino a un concepto encapsulado de ''algo''. Me gusta cómo se usa la palabra ''objeto'' cuando se dice programación orientada a objetos .
De todos modos, ''objetos'' tienen ciertos modificadores. Por ejemplo, sellado, de solo lectura, virtual, estático, etc. ... Esta lista se puede encontrar here . En el caso de un delegado, tiene el extra llamado evento . El evento lo hace de modo que cuando un delegado se declara como parte de una clase, solo expone los métodos de agregar y eliminar según el modificador de acceso dado al evento. Estos métodos están definidos en una naturaleza similar para obtener y establecer una propiedad. Otras operaciones del delegado (asignación, acceso de lectura, invocación de método, etc.) solo se permiten dentro de la clase en la que se declaró el delegado del evento. La otra cosa que encuentro interesante es que todos los delegados tienen los métodos Invoke, BeginInvoke y EndInvoke, pero no se puede navegar para verlos en Visual Studio, ni pude encontrar la documentación que los describe ...
Bueno. Entonces, después de saber todo eso, ¿cuál es la ventaja de utilizar la palabra clave del evento que no sea para modificar cómo se puede acceder al delegado? Parece que, en muchos casos, sería mejor simplemente declarar un delegado sin la palabra clave del evento. Una situación con la que me encontré recientemente es que quería crear una clase base abstracta que contuviera 2 eventos. Cualquier clase que derive de esta clase base debería ser capaz de usar los eventos como si fueran propios, similar a cualquier otro objeto de la clase que esté expuesto a la clase derivada (es decir, no privada, a menos que la clase derivada esté en otro ensamblado, y el objeto fue declarado interno).
Básicamente, quería que las clases derivadas utilizaran estos eventos como propios. La única forma de hacerlo era exponiendo la variable de respaldo de los eventos como protegida, para que las clases derivadas pudieran generar los eventos. Al mirar el código, esto parecía bastante estúpido ya que básicamente estaba definiendo al delegado dos veces; una vez como un campo protegido, y el otro como el evento público. Pensé,
¿No sería mejor hacer una clase llamada Evento que tenga un parámetro de salida de una Acción en el constructor? La acción que se devuelve es equivalente a Raise que muchos han realizado como método de extensión para delegados, donde verifica si el delegado es nulo y luego invoca al delegado. Los únicos métodos públicos en Evento serían Agregar y Eliminar para agregar delegados y eliminarlos del delegado subyacente (+ =, - =). Las clases podrían tener estos eventos como propiedades, como,
public Event SomethingHappened { get; private set; }
para que solo esa clase pueda reasignar el evento. O un campo público de solo lectura sería igual de efectivo. El parámetro out que se devuelve desde el constructor es almacenado por la clase y se llama cuando la clase quiere plantear el evento. Sé que es una solución alternativa, pero hará el trabajo, y permite que los eventos no solo se pasen como argumentos, sino que permitan que las clases derivadas invoquen el método Raise si la clase base lo define como protegido.
TLDR:
¿Cuál es la ventaja de usar la palabra clave del evento que no sea para modificar cómo se puede acceder al delegado?
Básicamente, quería que las clases derivadas utilizaran estos eventos como propios. La única forma de hacerlo era exponiendo la variable de respaldo de los eventos como protegida, para que las clases derivadas pudieran generar los eventos.
La forma habitual de manejar esto no es exponer el campo, sino exponer un método para plantear el evento.
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
var handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
Esto no solo permite que las clases derivadas generen el evento, sino que también permite que las clases derivadas hagan algo antes de que se llame a los manejadores suscritos.
Para responder a tu pregunta real, sin embargo:
¿Cuál es la ventaja de usar la palabra clave del evento que no sea para modificar cómo se puede acceder al delegado?
Una ventaja aún no mencionada (creo):
public event PropertyChangedEventHandler PropertyChanged;
puede ser cambiado a
public event PropertyChangedEventHandler PropertyChanged
{
add { /* custom code here */ }
remove { /* custom code here */ }
}
sin requerir una recompilación de todos los usuarios de su biblioteca. Es posible que desee algo como esto si luego encuentra una razón para no simplemente almacenar los controladores en un campo privado. Esta es la misma ventaja de una propiedad implementada automáticamente sobre un campo.
Creo que puede comparar fácilmente la palabra clave ''evento'' con los usuarios, pero tiene otros beneficios que provienen de la palabra clave ''evento'':
- más fácil de leer, todos saben lo que significa ''evento''
- menos trabajo, no es necesario crear funciones especiales para suscribirse y cancelar la suscripción de la variable de delegado
- agregar o quitar del delegado que tiene palabra clave ''evento'' tiene ''bloqueo'' (sincronización) agregado.
- IDE / frameworks pueden interpretar ''eventos'' y ayudarte