Para responder tu pregunta:
- La generación de un evento bloquea el hilo si todos los controladores de eventos se implementan sincrónicamente.
- Los controladores de eventos se ejecutan secuencialmente, uno tras otro, en el orden en que están suscritos al evento.
Yo también tenía curiosidad por el mecanismo interno de event
y sus operaciones relacionadas. Así que escribí un programa simple y solía ildasm
hurgar en su implementación.
La respuesta corta es
- no hay ninguna operación asincrónica involucrada en la suscripción o invocación de eventos.
- El 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 lujoso aquí.
Ahora las partes interesantes son:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
- y como
OnCall
se invoca enDo()
¿Cómo se implementa la suscripción y cancelación de suscripción?
Aquí está la add_OnCall
implementación abreviada en CIL. La parte interesante es que se utiliza 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
Asimismo, Delegate.Remove
se utiliza en remove_OnCall
.
¿Cómo se invoca un evento?
Para invocar OnCall
en Do()
, simplemente llama al delegado concatenada 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 exactamente un suscriptor a un evento?
Y finalmente, Main
no es sorprendente que la suscripción al OnCall
evento se realice llamando al add_OnCall
método en la Foo
instancia.