Una declaración de evento agrega una capa de abstracción y protección en la instancia de delegado . Esta protección evita que los clientes del delegado restablezcan el delegado y su lista de invocación y solo permite agregar o eliminar destinos de la lista de invocación.
Por supuesto, esta capa de protección también evita que los "clientes" (código fuera de la clase / estructura definitoria) invoquen al delegado y obtengan de cualquier forma el objeto delegado "detrás" del evento.
Jeppe Stig Nielsen
77
No del todo cierto. Puede declarar un evento sin una instancia de delegado de fondo. En c #, puede implementar un evento explícitamente y usar una estructura de datos de back-end diferente de su elección.
Miguel Gamboa
3
@mmcdole, ¿puedes dar un ejemplo para explicar el suyo?
vivek nuna
103
Para entender las diferencias, puede ver estos 2 ejemplos
Ejemplo con delegados (en este caso, una acción, que es un tipo de delegado que no devuelve un valor)
Animal animal=newAnimal();
animal.Run+=()=>Console.WriteLine("I'm running");
animal.Run+=()=>Console.WriteLine("I'm still running");
animal.RaiseEvent();
Este código funciona bien pero podría tener algunos puntos débiles.
Por ejemplo, si escribo esto:
animal.Run+=()=>Console.WriteLine("I'm running");
animal.Run+=()=>Console.WriteLine("I'm still running");
animal.Run=()=>Console.WriteLine("I'm sleeping");
con la última línea de código, he anulado los comportamientos anteriores solo con uno faltante +(he usado en =lugar de +=)
Otro punto débil es que cada clase que usa tu Animalclase puede aumentar RaiseEventsimplemente llamándola animal.RaiseEvent().
Para evitar estos puntos débiles, puede usar eventsen c #.
Tu clase de Animal cambiará de esta manera:
publicclassArgsSpecial:EventArgs{publicArgsSpecial(string val){Operation=val;}publicstringOperation{get;set;}}publicclassAnimal{// Empty delegate. In this way you are sure that value is always != null // because no one outside of the class can change it.publiceventEventHandler<ArgsSpecial>Run=delegate{}publicvoidRaiseEvent(){Run(this,newArgsSpecial("Run faster"));}}
para llamar eventos
Animal animal=newAnimal();
animal.Run+=(sender, e)=>Console.WriteLine("I'm running. My value is {0}", e.Operation);
animal.RaiseEvent();
Diferencias:
No está utilizando una propiedad pública sino un campo público (al usar eventos, el compilador protege sus campos del acceso no deseado)
Los eventos no se pueden asignar directamente. En este caso, no dará lugar al error anterior que he mostrado al anular el comportamiento.
Nadie fuera de su clase puede realizar el evento.
Los eventos pueden incluirse en una declaración de interfaz, mientras que un campo no puede
Notas:
EventHandler se declara como el siguiente delegado:
Todo se veía genial hasta que me encontré con "Nadie fuera de tu clase puede organizar el evento". Qué significa eso? ¿Nadie puede llamar RaiseEventmientras un método de llamada tenga acceso a una instancia animalen el código que usa el evento?
dance2die
11
@Sung Events solo se puede subir desde dentro de la clase, tal vez no he sido claro al explicar eso. Con eventos, puede llamar a la función que genera el evento (encapsulación), pero solo se puede elevar desde dentro de la clase que lo define. Avísame si no estoy claro.
@faby, ¿Quieres decir que a pesar de que el evento se declara público, todavía no puedo hacerlo animal.Run(this, new ArgsSpecial("Run faster");?
Pap
1
@ChieltenBrinke Por supuesto, el evento se puede asignar a los miembros de la clase ... pero no de otra manera.
Jim Balter
94
Además de las propiedades sintácticas y operativas, también hay una diferencia semántica.
Los delegados son, conceptualmente, plantillas de funciones; es decir, expresan un contrato al que debe adherirse una función para ser considerado del "tipo" del delegado.
Los eventos representan ... bueno, eventos. Su objetivo es alertar a alguien cuando sucede algo y sí, se adhieren a una definición de delegado, pero no son lo mismo.
Incluso si fueran exactamente lo mismo (sintácticamente y en el código IL), seguirá habiendo la diferencia semántica. En general, prefiero tener dos nombres diferentes para dos conceptos diferentes, incluso si se implementan de la misma manera (lo que no significa que me guste tener el mismo código dos veces).
Entonces, ¿podríamos decir que un evento es un tipo "especial" de delegado?
Pap
No entiendo su punto. Puede usar un delegado para 'alertar a alguien cuando algo sucede'. Quizás no haría eso, pero puede hacerlo y, por lo tanto, no es una propiedad inherente del evento.
Steve
@Jorge Córdoba ejemplo de delegado y delegado de eventos es propietario de un periódico y eventos (suscribirse o cancelar suscripción) y algunas personas compran el periódico y algunas personas no compran el periódico significa que el propietario del periódico no puede obligar a todas y cada una de las personas a comprar el periódico mi punto ¿bien o mal?
Brevemente, lo quitamos del artículo: los eventos son encapsulados sobre delegados.
Cita del artículo:
Supongamos que los eventos no existen como un concepto en C # /. NET. ¿Cómo se suscribiría otra clase a un evento? Tres opciones:
Una variable delegada pública
Una variable delegada respaldada por una propiedad
Una variable delegada con los métodos AddXXXHandler y RemoveXXXHandler
La opción 1 es claramente horrible, por todas las razones normales por las que aborrecemos las variables públicas.
La opción 2 es un poco mejor, pero permite que los suscriptores se anulen entre sí de manera efectiva; sería demasiado fácil escribir someInstance.MyEvent = eventHandler; que reemplazaría cualquier controlador de eventos existente en lugar de agregar uno nuevo. Además, aún necesita escribir las propiedades.
La opción 3 es básicamente lo que le brindan los eventos, pero con una convención garantizada (generada por el compilador y respaldada por banderas adicionales en el IL) y una implementación "gratuita" si está satisfecho con la semántica que le brindan los eventos similares a los de un campo. Suscribirse y darse de baja de eventos se encapsula sin permitir el acceso arbitrario a la lista de controladores de eventos, y los idiomas pueden simplificar las cosas al proporcionar sintaxis tanto para la declaración como para la suscripción.
Esto es más una preocupación teórica que otra cosa, pero FWIW siempre sentí que el argumento "La opción 1 es mala porque no nos gustan las variables públicas" podría usar un poco más de aclaración. Si dice eso porque es una "mala práctica de OOP", técnicamente una public Delegatevariable estaría exponiendo "datos", pero que yo sepa, OOP nunca mencionó ningún concepto como un Delegate(no es un "objeto" ni un "mensaje") , y .NET realmente apenas trata a los delegados como datos de todos modos.
jrh
Aunque también me gustaría darle consejos más prácticos, si se encuentra en una situación en la que desea asegurarse de que solo haya un controlador, crear sus propios AddXXXHandlermétodos con una private Delegatevariable puede ser una buena opción. En este caso, puede verificar si un controlador ya está configurado y reaccionar adecuadamente. Esto también puede ser una buena configuración si necesita que el objeto que contiene el Delegatepueda borrar todos los controladores ( eventno le da ninguna forma de hacerlo).
jrh
7
NOTA: Si tiene acceso a C # 5.0 desatado , lea las "Limitaciones en el uso simple de delegados" en el Capítulo 18 titulado "Eventos" para comprender mejor las diferencias entre los dos.
Siempre me ayuda a tener un ejemplo simple y concreto. Así que aquí hay uno para la comunidad. Primero muestro cómo puedes usar delegados solos para hacer lo que los Eventos hacen por nosotros. Luego muestro cómo funcionaría la misma solución con una instancia de EventHandler. Y luego explico por qué NO queremos hacer lo que explico en el primer ejemplo. Esta publicación fue inspirada por un artículo de John Skeet.
Ejemplo 1: uso de delegado público
Supongamos que tengo una aplicación WinForms con un solo cuadro desplegable. El menú desplegable está vinculado a un List<Person>. Donde la persona tiene propiedades de Id, Nombre, Apodo, HairColor. En el formulario principal hay un control de usuario personalizado que muestra las propiedades de esa persona. Cuando alguien selecciona a una persona en el menú desplegable, las etiquetas de la actualización de control de usuario muestran las propiedades de la persona seleccionada.
Así es como funciona eso. Tenemos tres archivos que nos ayudan a armar esto:
Mediator.cs: la clase estática contiene los delegados
Form1.cs - formulario principal
DetailView.cs: el control de usuario muestra todos los detalles
Aquí está el código relevante para cada una de las clases:
classMediator{publicdelegatevoidPersonChangedDelegate(Person p);//delegate type definitionpublicstaticPersonChangedDelegatePersonChangedDel;//delegate instance. Detail view will "subscribe" to this.publicstaticvoidOnPersonChanged(Person p)//Form1 will call this when the drop-down changes.{if(PersonChangedDel!=null){PersonChangedDel(p);}}}
Finalmente tenemos el siguiente código en nuestro Form1.cs. Aquí estamos llamando a OnPersonChanged, que llama a cualquier código suscrito al delegado.
privatevoid comboBox1_SelectedIndexChanged(object sender,EventArgs e){Mediator.OnPersonChanged((Person)comboBox1.SelectedItem);//Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.}
Okay. Así es como lo haría funcionar sin usar eventos y solo con delegados . Simplemente ponemos un delegado público en una clase; puede hacerlo estático o un singleton, o lo que sea. Excelente.
PERO, PERO, PERO, no queremos hacer lo que acabo de describir arriba. Porque los campos públicos son malos por muchas, muchas razones. ¿Entonces, cuales son nuestras opciones? Como describe John Skeet, estas son nuestras opciones:
Una variable delegada pública (esto es lo que acabamos de hacer arriba. No hagas esto. Solo te dije arriba por qué es malo)
Coloque el delegado en una propiedad con un get / set (el problema aquí es que los suscriptores podrían anularse entre sí, por lo que podríamos suscribir un montón de métodos al delegado y luego podríamos decir accidentalmente PersonChangedDel = null, eliminando todas las otras suscripciones. Otro problema que queda aquí es que, dado que los usuarios tienen acceso al delegado, pueden invocar los objetivos en la lista de invocación; no queremos que los usuarios externos tengan acceso a cuándo realizar nuestros eventos.
Una variable delegada con los métodos AddXXXHandler y RemoveXXXHandler
Esta tercera opción es esencialmente lo que nos brinda un evento. Cuando declaramos un EventHandler, nos da acceso a un delegado, no públicamente, no como una propiedad, sino que llamamos a un evento que simplemente agrega / elimina accesores.
Veamos cómo se ve el mismo programa, pero ahora usando un Evento en lugar del delegado público (también he cambiado nuestro Mediador a un singleton):
Ejemplo 2: con EventHandler en lugar de un delegado público
Mediador:
classMediator{privatestaticreadonlyMediator_Instance=newMediator();privateMediator(){}publicstaticMediatorGetInstance(){return_Instance;}publiceventEventHandler<PersonChangedEventArgs>PersonChanged;//this is just a property we expose to add items to the delegate.publicvoidOnPersonChanged(object sender,Person p){var personChangedDelegate =PersonChangedasEventHandler<PersonChangedEventArgs>;if(personChangedDelegate !=null){
personChangedDelegate(sender,newPersonChangedEventArgs(){Person= p });}}}
Tenga en cuenta que si usa F12 en EventHandler, le mostrará que la definición es solo un delegado genérico con el objeto adicional "remitente":
Aunque aprecio todo el trabajo bueno en este post y me gustaba leer la mayor parte de ella, todavía me siento un problema no se aborda - The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events. En la última versión de la Mediator, aún puede llamar al OnPersonChangecuando tenga una referencia al singleton. Tal vez debería mencionar que el Mediatorenfoque no evita ese comportamiento en particular y está más cerca de un autobús de eventos.
Ivaylo Slavov
6
También puede usar eventos en declaraciones de interfaz, no así para los delegados.
La interfaz @surfen puede contener eventos, pero no delegados.
Alexandr Nikitin
1
Qué quieres decir exactamente? Puede tener Action a { get; set; }dentro de una definición de interfaz.
Chiel ten Brinke
6
¡Qué gran malentendido entre eventos y delegados! Un delegado especifica un TIPO (como a class, o interfacedoes), mientras que un evento es solo un tipo de MIEMBRO (como campos, propiedades, etc.). Y, al igual que cualquier otro tipo de miembro, un evento también tiene un tipo. Sin embargo, en el caso de un evento, el tipo de evento debe ser especificado por un delegado. Por ejemplo, NO PUEDE declarar un evento de un tipo definido por una interfaz.
Concluyendo, podemos hacer la siguiente Observación: el tipo de evento DEBE ser definido por un delegado . Esta es la relación principal entre un evento y un delegado y se describe en la sección II.18 Definición de eventos de las Particiones I a VI de ECMA-335 (CLI) :
En el uso típico, TypeSpec (si está presente) identifica a un delegado cuya firma coincide con los argumentos pasados al método de disparo del evento.
Sin embargo, este hecho NO implica que un evento use un campo delegado de respaldo . En verdad, un evento puede usar un campo de respaldo de cualquier tipo de estructura de datos que elija. Si implementa un evento explícitamente en C #, puede elegir la forma en que almacena los controladores de eventos (tenga en cuenta que los controladores de eventos son instancias del tipo de evento , que a su vez es obligatoriamente un tipo de delegado --- de la Observación anterior ) Sin embargo, puede almacenar esos controladores de eventos (que son instancias de delegado) en una estructura de datos como Listao a Dictionaryo cualquier otro, o incluso en un campo de delegado de respaldo. Pero no olvide que NO es obligatorio que use un campo delegado.
Un evento en .net es una combinación designada de un método Add y un método Remove, que esperan algún tipo particular de delegado. Tanto C # como vb.net pueden generar automáticamente código para los métodos de agregar y quitar que definirán a un delegado para mantener las suscripciones de eventos, y agregar / quitar lo pasado en delegagte a / desde ese delegado de suscripción. VB.net también generará automáticamente código (con la declaración RaiseEvent) para invocar la lista de suscripción si y solo si no está vacía; por alguna razón, C # no genera este último.
Tenga en cuenta que si bien es común administrar las suscripciones de eventos utilizando un delegado de multidifusión, ese no es el único medio para hacerlo. Desde una perspectiva pública, un posible suscriptor de eventos necesita saber cómo hacer que un objeto sepa que quiere recibir eventos, pero no necesita saber qué mecanismo utilizará el editor para generar los eventos. Tenga en cuenta también que, aunque quien definió la estructura de datos de eventos en .net aparentemente pensó que debería haber un medio público para generarlos, ni C # ni vb.net utilizan esa función.
El evento es una REFERENCIA a un delegado con dos restricciones.
No se puede invocar directamente
No se pueden asignar valores directamente (por ejemplo, eventObj = delegateMethod)
Por encima de dos son los puntos débiles para los delegados y se aborda en caso de evento. El ejemplo de código completo para mostrar la diferencia en fiddler está aquí https://dotnetfiddle.net/5iR3fB .
Alternar el comentario entre Evento y Delegar y el código del cliente que invoca / asigna valores a delegar para comprender la diferencia
Aquí está el código en línea.
/*
This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
Event is an delegate reference with two restrictions for increased protection
1. Cannot be invoked directly
2. Cannot assign value to delegate reference directly
Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/publicclassRoomTemperatureController{privateint _roomTemperature =25;//Default/Starting room Temperatureprivatebool _isAirConditionTurnedOn =false;//Default AC is Offprivatebool _isHeatTurnedOn =false;//Default Heat is Offprivatebool _tempSimulator =false;publicdelegatevoidOnRoomTemperatureChange(int roomTemperature);//OnRoomTemperatureChange is a type of Delegate (Check next line for proof)// public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), publiceventOnRoomTemperatureChangeWhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), publicRoomTemperatureController(){WhenRoomTemperatureChange+=InternalRoomTemperatuerHandler;}privatevoidInternalRoomTemperatuerHandler(int roomTemp){System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");}//User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)publicboolTurnRoomTeperatureSimulator{set{
_tempSimulator =value;if(value){SimulateRoomTemperature();//Turn on Simulator }}get{return _tempSimulator;}}publicvoidTurnAirCondition(bool val){
_isAirConditionTurnedOn = val;
_isHeatTurnedOn =!val;//Binary switch If Heat is ON - AC will turned off automatically (binary)System.Console.WriteLine("Aircondition :"+ _isAirConditionTurnedOn);System.Console.WriteLine("Heat :"+ _isHeatTurnedOn);}publicvoidTurnHeat(bool val){
_isHeatTurnedOn = val;
_isAirConditionTurnedOn =!val;//Binary switch If Heat is ON - AC will turned off automatically (binary)System.Console.WriteLine("Aircondition :"+ _isAirConditionTurnedOn);System.Console.WriteLine("Heat :"+ _isHeatTurnedOn);}publicasyncvoidSimulateRoomTemperature(){while(_tempSimulator){if(_isAirConditionTurnedOn)
_roomTemperature--;//Decrease Room Temperature if AC is turned Onif(_isHeatTurnedOn)
_roomTemperature++;//Decrease Room Temperature if AC is turned OnSystem.Console.WriteLine("Temperature :"+ _roomTemperature);if(WhenRoomTemperatureChange!=null)WhenRoomTemperatureChange(_roomTemperature);System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status}}}publicclassMySweetHome{RoomTemperatureController roomController =null;publicMySweetHome(){
roomController =newRoomTemperatureController();
roomController.WhenRoomTemperatureChange+=TurnHeatOrACBasedOnTemp;//roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.//roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
roomController.SimulateRoomTemperature();System.Threading.Thread.Sleep(5000);
roomController.TurnAirCondition(true);
roomController.TurnRoomTeperatureSimulator=true;}publicvoidTurnHeatOrACBasedOnTemp(int temp){if(temp >=30)
roomController.TurnAirCondition(true);if(temp <=15)
roomController.TurnHeat(true);}publicstaticvoidMain(string[]args){MySweetHome home =newMySweetHome();}}
Si marca Intermediate Language, sabrá que el compilador .net convierte delegado a una clase sellada en IL con algunas funciones incorporadas, como invoke, beginInvoke, endInvoke y delegate heredadas de otra clase, tal vez llamada "SystemMulticast". Supongo que Event es una clase secundaria de Delegado con algunas propiedades adicionales.
La diferencia entre la instancia del evento y el delegado es que no puede ejecutar el evento fuera de la declaración. Si declara un evento en la clase A, solo puede ejecutar este evento en la clase A. Si declara un delegado en la Clase A, puede usarlo en cualquier lugar. Creo que esta es la principal diferencia entre ellos.
Respuestas:
Una declaración de evento agrega una capa de abstracción y protección en la instancia de delegado . Esta protección evita que los clientes del delegado restablezcan el delegado y su lista de invocación y solo permite agregar o eliminar destinos de la lista de invocación.
fuente
Para entender las diferencias, puede ver estos 2 ejemplos
Ejemplo con delegados (en este caso, una acción, que es un tipo de delegado que no devuelve un valor)
Para usar el delegado, debe hacer algo como esto:
Este código funciona bien pero podría tener algunos puntos débiles.
Por ejemplo, si escribo esto:
con la última línea de código, he anulado los comportamientos anteriores solo con uno faltante
+
(he usado en=
lugar de+=
)Otro punto débil es que cada clase que usa tu
Animal
clase puede aumentarRaiseEvent
simplemente llamándolaanimal.RaiseEvent()
.Para evitar estos puntos débiles, puede usar
events
en c #.Tu clase de Animal cambiará de esta manera:
para llamar eventos
Diferencias:
Notas:
EventHandler se declara como el siguiente delegado:
toma un remitente (de tipo Objeto) y argumentos de evento. El remitente es nulo si proviene de métodos estáticos.
Este ejemplo, que usa
EventHandler<ArgsSpecial>
, también se puede escribir usando en suEventHandler
lugar.Consulte aquí para obtener documentación sobre EventHandler
fuente
RaiseEvent
mientras un método de llamada tenga acceso a una instanciaanimal
en el código que usa el evento?animal.Run(this, new ArgsSpecial("Run faster");
?Además de las propiedades sintácticas y operativas, también hay una diferencia semántica.
Los delegados son, conceptualmente, plantillas de funciones; es decir, expresan un contrato al que debe adherirse una función para ser considerado del "tipo" del delegado.
Los eventos representan ... bueno, eventos. Su objetivo es alertar a alguien cuando sucede algo y sí, se adhieren a una definición de delegado, pero no son lo mismo.
Incluso si fueran exactamente lo mismo (sintácticamente y en el código IL), seguirá habiendo la diferencia semántica. En general, prefiero tener dos nombres diferentes para dos conceptos diferentes, incluso si se implementan de la misma manera (lo que no significa que me guste tener el mismo código dos veces).
fuente
Aquí hay otro buen enlace para consultar. http://csharpindepth.com/Articles/Chapter2/Events.aspx
Brevemente, lo quitamos del artículo: los eventos son encapsulados sobre delegados.
Cita del artículo:
fuente
public Delegate
variable estaría exponiendo "datos", pero que yo sepa, OOP nunca mencionó ningún concepto como unDelegate
(no es un "objeto" ni un "mensaje") , y .NET realmente apenas trata a los delegados como datos de todos modos.AddXXXHandler
métodos con unaprivate Delegate
variable puede ser una buena opción. En este caso, puede verificar si un controlador ya está configurado y reaccionar adecuadamente. Esto también puede ser una buena configuración si necesita que el objeto que contiene elDelegate
pueda borrar todos los controladores (event
no le da ninguna forma de hacerlo).NOTA: Si tiene acceso a C # 5.0 desatado , lea las "Limitaciones en el uso simple de delegados" en el Capítulo 18 titulado "Eventos" para comprender mejor las diferencias entre los dos.
Siempre me ayuda a tener un ejemplo simple y concreto. Así que aquí hay uno para la comunidad. Primero muestro cómo puedes usar delegados solos para hacer lo que los Eventos hacen por nosotros. Luego muestro cómo funcionaría la misma solución con una instancia de
EventHandler
. Y luego explico por qué NO queremos hacer lo que explico en el primer ejemplo. Esta publicación fue inspirada por un artículo de John Skeet.Ejemplo 1: uso de delegado público
Supongamos que tengo una aplicación WinForms con un solo cuadro desplegable. El menú desplegable está vinculado a un
List<Person>
. Donde la persona tiene propiedades de Id, Nombre, Apodo, HairColor. En el formulario principal hay un control de usuario personalizado que muestra las propiedades de esa persona. Cuando alguien selecciona a una persona en el menú desplegable, las etiquetas de la actualización de control de usuario muestran las propiedades de la persona seleccionada.Así es como funciona eso. Tenemos tres archivos que nos ayudan a armar esto:
Aquí está el código relevante para cada una de las clases:
Aquí está nuestro control de usuario:
Finalmente tenemos el siguiente código en nuestro Form1.cs. Aquí estamos llamando a OnPersonChanged, que llama a cualquier código suscrito al delegado.
Okay. Así es como lo haría funcionar sin usar eventos y solo con delegados . Simplemente ponemos un delegado público en una clase; puede hacerlo estático o un singleton, o lo que sea. Excelente.
PERO, PERO, PERO, no queremos hacer lo que acabo de describir arriba. Porque los campos públicos son malos por muchas, muchas razones. ¿Entonces, cuales son nuestras opciones? Como describe John Skeet, estas son nuestras opciones:
PersonChangedDel = null
, eliminando todas las otras suscripciones. Otro problema que queda aquí es que, dado que los usuarios tienen acceso al delegado, pueden invocar los objetivos en la lista de invocación; no queremos que los usuarios externos tengan acceso a cuándo realizar nuestros eventos.Esta tercera opción es esencialmente lo que nos brinda un evento. Cuando declaramos un EventHandler, nos da acceso a un delegado, no públicamente, no como una propiedad, sino que llamamos a un evento que simplemente agrega / elimina accesores.
Veamos cómo se ve el mismo programa, pero ahora usando un Evento en lugar del delegado público (también he cambiado nuestro Mediador a un singleton):
Ejemplo 2: con EventHandler en lugar de un delegado público
Mediador:
Tenga en cuenta que si usa F12 en EventHandler, le mostrará que la definición es solo un delegado genérico con el objeto adicional "remitente":
El control del usuario:
Finalmente, aquí está el código Form1.cs:
Debido a que EventHandler quiere y EventArgs como parámetro, creé esta clase con solo una propiedad:
Esperemos que eso muestre un poco sobre por qué tenemos eventos y cómo son diferentes, pero funcionalmente iguales, como delegados.
fuente
The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events
. En la última versión de laMediator
, aún puede llamar alOnPersonChange
cuando tenga una referencia al singleton. Tal vez debería mencionar que elMediator
enfoque no evita ese comportamiento en particular y está más cerca de un autobús de eventos.También puede usar eventos en declaraciones de interfaz, no así para los delegados.
fuente
Action a { get; set; }
dentro de una definición de interfaz.¡Qué gran malentendido entre eventos y delegados! Un delegado especifica un TIPO (como a
class
, ointerface
does), mientras que un evento es solo un tipo de MIEMBRO (como campos, propiedades, etc.). Y, al igual que cualquier otro tipo de miembro, un evento también tiene un tipo. Sin embargo, en el caso de un evento, el tipo de evento debe ser especificado por un delegado. Por ejemplo, NO PUEDE declarar un evento de un tipo definido por una interfaz.Concluyendo, podemos hacer la siguiente Observación: el tipo de evento DEBE ser definido por un delegado . Esta es la relación principal entre un evento y un delegado y se describe en la sección II.18 Definición de eventos de las Particiones I a VI de ECMA-335 (CLI) :
Sin embargo, este hecho NO implica que un evento use un campo delegado de respaldo . En verdad, un evento puede usar un campo de respaldo de cualquier tipo de estructura de datos que elija. Si implementa un evento explícitamente en C #, puede elegir la forma en que almacena los controladores de eventos (tenga en cuenta que los controladores de eventos son instancias del tipo de evento , que a su vez es obligatoriamente un tipo de delegado --- de la Observación anterior ) Sin embargo, puede almacenar esos controladores de eventos (que son instancias de delegado) en una estructura de datos como
List
ao aDictionary
o cualquier otro, o incluso en un campo de delegado de respaldo. Pero no olvide que NO es obligatorio que use un campo delegado.fuente
Un evento en .net es una combinación designada de un método Add y un método Remove, que esperan algún tipo particular de delegado. Tanto C # como vb.net pueden generar automáticamente código para los métodos de agregar y quitar que definirán a un delegado para mantener las suscripciones de eventos, y agregar / quitar lo pasado en delegagte a / desde ese delegado de suscripción. VB.net también generará automáticamente código (con la declaración RaiseEvent) para invocar la lista de suscripción si y solo si no está vacía; por alguna razón, C # no genera este último.
Tenga en cuenta que si bien es común administrar las suscripciones de eventos utilizando un delegado de multidifusión, ese no es el único medio para hacerlo. Desde una perspectiva pública, un posible suscriptor de eventos necesita saber cómo hacer que un objeto sepa que quiere recibir eventos, pero no necesita saber qué mecanismo utilizará el editor para generar los eventos. Tenga en cuenta también que, aunque quien definió la estructura de datos de eventos en .net aparentemente pensó que debería haber un medio público para generarlos, ni C # ni vb.net utilizan esa función.
fuente
Para definir sobre el evento de manera simple:
El evento es una REFERENCIA a un delegado con dos restricciones.
Por encima de dos son los puntos débiles para los delegados y se aborda en caso de evento. El ejemplo de código completo para mostrar la diferencia en fiddler está aquí https://dotnetfiddle.net/5iR3fB .
Alternar el comentario entre Evento y Delegar y el código del cliente que invoca / asigna valores a delegar para comprender la diferencia
Aquí está el código en línea.
fuente
Delegate es un puntero de función de tipo seguro. El evento es una implementación del patrón de diseño editor-suscriptor usando delegado.
fuente
Si marca Intermediate Language, sabrá que el compilador .net convierte delegado a una clase sellada en IL con algunas funciones incorporadas, como invoke, beginInvoke, endInvoke y delegate heredadas de otra clase, tal vez llamada "SystemMulticast". Supongo que Event es una clase secundaria de Delegado con algunas propiedades adicionales.
La diferencia entre la instancia del evento y el delegado es que no puede ejecutar el evento fuera de la declaración. Si declara un evento en la clase A, solo puede ejecutar este evento en la clase A. Si declara un delegado en la Clase A, puede usarlo en cualquier lugar. Creo que esta es la principal diferencia entre ellos.
fuente