remarks example cref c# .net events delegates

example - remarks c#



¿los eventos C#son sincrónicos? (7)

Hay dos partes en esta pregunta:

  1. ¿El bloqueo de un evento bloquea el hilo, o inicia la ejecución de EventHandlers de forma asíncrona y el hilo continúa continúa al mismo tiempo?

  2. ¿Los EventHandlers individuales (suscritos al evento) se ejecutan de forma síncrona uno tras otro, o se ejecutan de forma asincrónica sin garantía de que otros no se estén ejecutando al mismo tiempo?



Esta es una respuesta general y refleja el comportamiento predeterminado:

  1. Sí, bloquea el hilo, si los métodos que se suscriben al evento no son asincrónicos.
  2. Se ejecutan uno después del otro. Esto tiene otro giro: si un manejador de eventos arroja una excepción, los manejadores de eventos aún no ejecutados no se ejecutarán.

Una vez dicho esto, cada clase que ofrece eventos puede elegir implementar su evento de forma asincrónica. IDesign proporciona una clase llamada EventsHelper que simplifica esto.

[Nota] Este enlace requiere que proporciones una dirección de correo electrónico para descargar la clase EventsHelper.


Los delegados suscritos al evento se invocan sincrónicamente en el orden en que se agregaron. Si uno de los delegados lanza una excepción, los siguientes no serán llamados.

Dado que los eventos se definen con delegados de multidifusión, puede escribir su propio mecanismo de disparo utilizando

Delegate.GetInvocationList();

e invocar a los delegados de forma asincrónica;


Los eventos en C # se ejecutan sincrónicamente (en ambos casos), siempre que no se inicie manualmente un segundo subproceso.


Los eventos son sincrónicos. Esta es la razón por la cual el ciclo de vida del evento funciona de la manera que lo hace. Sucede antes de las cargas, las cargas suceden antes de los renders, etc.

Si no se especifica ningún controlador para un evento, el ciclo simplemente se abre paso. Si se especifica más de un manejador, serán llamados en orden y uno no podrá continuar hasta que el otro esté completamente terminado.

Incluso las llamadas asincrónicas son sincrónicas a un grado. Sería imposible llamar al final antes de que se complete el inicio.


Los eventos son solo arreglos de delegados. Siempre que la llamada delegada sea sincrónica, los eventos también son sincrónicos.


Para responder tu pregunta:

  1. Criar un evento bloquea el hilo si todos los controladores de eventos están implementados de forma sincronizada.
  2. Los controladores de eventos se ejecutan secuencialmente, uno tras otro, en el orden en que se suscriben al evento.

Yo también tenía curiosidad sobre el mecanismo interno del event y sus operaciones relacionadas. Así que escribí un programa simple y usé ildasm para ildasm su implementación.

La respuesta corta es

  • no hay ninguna operación asincrónica involucrada en la suscripción o invocación de los eventos.
  • evento se implementa con un campo de delegado de respaldo del mismo tipo de delegado
  • la suscripción se realiza con Delegate.Combine()
  • darse de baja se hace con Delegate.Remove()
  • La invocación se realiza simplemente invocando al delegado combinado final

Esto es lo que hice. El programa que utilicé:

public class Foo { // cool, it can return a value! which value it returns if there''re multiple // subscribers? answer (by trying): the last subscriber. public event Func<int, string> OnCall; private int val = 1; public void Do() { if (OnCall != null) { var res = OnCall(val++); Console.WriteLine($"publisher got back a {res}"); } } } public class Program { static void Main(string[] args) { var foo = new Foo(); foo.OnCall += i => { Console.WriteLine($"sub2: I''ve got a {i}"); return "sub2"; }; foo.OnCall += i => { Console.WriteLine($"sub1: I''ve got a {i}"); return "sub1"; }; foo.Do(); foo.Do(); } }

Aquí está la implementación de Foo:

Tenga en cuenta que hay un campo OnCall y un evento OnCall . El campo OnCall es obviamente la propiedad de respaldo. Y es simplemente un Func<int, string> , nada elegante aquí.

Ahora las partes interesantes son:

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • y cómo se invoca OnCall en Do()

¿Cómo se implementan las suscripciones y las anulaciones?

Aquí está la implementación abreviada add_OnCall en CIL. La parte interesante es que usa Delegate.Combine() para concatenar dos delegados.

.method public hidebysig specialname instance void add_OnCall(class [mscorlib]System.Func`2<int32,string> ''value'') cil managed { // ... .locals init (class [mscorlib]System.Func`2<int32,string> V_0, class [mscorlib]System.Func`2<int32,string> V_1, class [mscorlib]System.Func`2<int32,string> V_2) IL_0000: ldarg.0 IL_0001: ldfld class [mscorlib]System.Func`2<int32,string> ConsoleApp1.Foo::OnCall // ... IL_000b: call class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate) // ... } // end of method Foo::add_OnCall

Del mismo modo, Delegate.Remove se usa en remove_OnCall .

¿Cómo se invoca un evento?

Para invocar OnCall en Do() , simplemente llama al delegado concatenado final después de cargar el arg:

IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)

¿Cómo se suscribe un suscriptor exactamente a un evento?

Y finalmente, en Main , no sorprendentemente, la suscripción al evento OnCall se hace llamando add_OnCall método add_OnCall en la instancia de Foo .