Aby odpowiedzieć na Twoje pytania:
- Podniesienie zdarzenia powoduje zablokowanie wątku, jeśli wszystkie programy obsługi zdarzeń są zaimplementowane synchronicznie.
- Procedury obsługi zdarzeń są wykonywane sekwencyjnie, jeden po drugim, w kolejności, w jakiej zostały zasubskrybowane do zdarzenia.
Ja też byłem ciekawy wewnętrznego mechanizmu eventi związanych z nim operacji. Napisałem więc prosty program i przeglądałem ildasmjego implementację.
Krótka odpowiedź brzmi
- nie ma operacji asynchronicznej zaangażowanej w subskrybowanie lub wywoływanie zdarzeń.
- zdarzenie jest implementowane przy użyciu pola delegata zapasowego tego samego typu
- subskrybowanie jest zakończone
Delegate.Combine()
- rezygnacja z subskrypcji jest zakończona
Delegate.Remove()
- Wywołanie odbywa się przez proste wywołanie ostatecznego połączonego delegata
Oto co zrobiłem. Program, którego użyłem:
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();
}
}
Oto implementacja Foo:

Zauważ, że jest tam pole OnCall i wydarzenie OnCall . Pole OnCalljest oczywiście właściwością podkładową. I to jest po prostu Func<int, string>nic nadzwyczajnego.
Teraz interesujące części to:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
- i jak
OnCalljest wzywanyDo()
W jaki sposób realizowane jest subskrybowanie i anulowanie subskrypcji?
Oto skrócona add_OnCallimplementacja w CIL. Interesujące jest to, że używa go Delegate.Combinedo łączenia dwóch delegatów.
.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
Podobnie Delegate.Removejest używany w remove_OnCall.
Jak wywoływane jest wydarzenie?
Aby wywołać OnCallw Do(), po prostu wywołuje ostatecznego połączonego delegata po załadowaniu argumentu:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
Jak dokładnie subskrybent subskrybuje wydarzenie?
I wreszcie, co Mainnie jest zaskakujące, subskrybowanie OnCallzdarzenia odbywa się poprzez wywołanie add_OnCallmetody w Fooinstancji.