这个问题有两个部分:
raise事件是否会阻塞线程,或者它是否开始异步执行EventHandler并且线程同时继续运行?
各个事件处理程序(订阅事件)是相继同步运行,还是异步运行且不能保证其他事件处理程序不会同时运行?
IDesign 提供了一个名为 EventsHelper
[注意] 此链接要求您提供电子邮件地址才能下载 EventsHelper 类。 (我没有任何关系)
回答您的问题:
event
的内部机制以及相关操作。所以我写了一个简单的程序并使用
ildasm
来探索它的实现。简短的答案是
Delegate.Combine()
完成
Delegate.Remove()
完成
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();
}
}
这是 Foo 的实现:请注意,有一个
字段 OnCall
和一个事件
OnCall
。字段
OnCall
显然是支持属性。这只是一个
Func<int, string>
,没什么特别的。现在有趣的部分是:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
OnCall
中调用 Do()
add_OnCall
实现。有趣的是它使用
Delegate.Combine
连接两个委托。
.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
同样,Delegate.Remove
用于
remove_OnCall
。事件是如何被调用的?
OnCall
中调用
Do()
,它只需在加载参数后调用最终的串联委托:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
订阅者到底如何订阅事件?Main
中,毫不奇怪,订阅
OnCall
事件是通过在
add_OnCall
实例上调用
Foo
方法来完成的。
之后的委托将不会被调用。
由于事件是使用多播委托定义的,因此您可以使用编写自己的触发机制
Delegate.GetInvocationList();
并异步调用委托;
如果没有为事件指定处理程序,则循环会继续进行。如果指定了多个处理程序,它们将按顺序调用,并且一个处理程序无法继续,直到另一个处理程序完全完成为止。
即使是异步调用在某种程度上也是同步的。在开始完成之前,不可能称其为结束。