Pour répondre à tes questions:
- Le déclenchement d'un événement bloque le thread si les gestionnaires d'événements sont tous implémentés de manière synchrone.
- Les gestionnaires d'événements sont exécutés séquentiellement, les uns après les autres, dans l'ordre dans lequel ils sont abonnés à l'événement.
J'étais moi aussi curieux de connaître le mécanisme interne de eventet ses opérations connexes. J'ai donc écrit un programme simple et utilisé ildasmpour fouiller autour de son implémentation.
La réponse courte est
- il n'y a aucune opération asynchrone impliquée dans l'abonnement ou l'appel des événements.
- l'événement est implémenté avec un champ de délégué de support du même type de délégué
- l'abonnement se fait avec
Delegate.Combine()
- la désinscription se fait avec
Delegate.Remove()
- L'appel se fait simplement en invoquant le délégué combiné final
Voici ce que j'ai fait. Le programme que j'ai utilisé:
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();
}
}
Voici l'implémentation de Foo:

Notez qu'il existe un champ OnCall et un événement OnCall . Le champ OnCallest évidemment la propriété de support. Et c'est simplement un Func<int, string>, rien d'extraordinaire ici.
Maintenant, les parties intéressantes sont:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
- et comment
OnCallest invoqué dansDo()
Comment l'abonnement et la désinscription sont-ils mis en œuvre?
Voici l' add_OnCallimplémentation abrégée dans CIL. La partie intéressante est qu'il utilise Delegate.Combinepour concaténer deux délégués.
.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
De même, Delegate.Removeest utilisé dans remove_OnCall.
Comment un événement est-il invoqué?
Pour invoquer OnCallin Do(), il appelle simplement le délégué concaténé final après le chargement de l'arg:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
Comment exactement un abonné s'abonne-t-il à un événement?
Et enfin, Mainsans surprise, l'inscription à l' OnCallévénement se fait en appelant add_OnCallmethod sur l' Fooinstance.