Để trả lời câu hỏi của bạn:
- Việc nâng một sự kiện sẽ chặn luồng nếu tất cả các trình xử lý sự kiện đều được triển khai đồng bộ.
- Các trình xử lý sự kiện được thực hiện tuần tự, cái khác, theo thứ tự chúng được đăng ký vào sự kiện.
Tôi cũng tò mò về cơ chế bên trong event
và các hoạt động liên quan của nó. Vì vậy, tôi đã viết một chương trình đơn giản và sử dụng ildasm
để xem xét việc triển khai nó.
Câu trả lời ngắn gọn là
- không có hoạt động không đồng bộ nào liên quan đến việc đăng ký hoặc gọi các sự kiện.
- sự kiện được triển khai với trường đại biểu ủng hộ của cùng một loại đại biểu
- đăng ký được thực hiện với
Delegate.Combine()
- hủy đăng ký được thực hiện với
Delegate.Remove()
- Việc gọi được thực hiện bằng cách chỉ cần gọi đại biểu kết hợp cuối cùng
Đây là những gì tôi đã làm. Chương trình tôi đã sử dụng:
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();
}
}
Đây là cách triển khai của Foo:
Lưu ý rằng có một trường OnCall
và một sự kiện OnCall
. Lĩnh vực OnCall
rõ ràng là tài sản hỗ trợ. Và nó chỉ đơn thuần là một Func<int, string>
, không có gì lạ mắt ở đây.
Bây giờ các phần thú vị là:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
- và làm thế nào
OnCall
được gọi trongDo()
Đăng ký và Hủy đăng ký được triển khai như thế nào?
Đây là cách add_OnCall
triển khai viết tắt trong CIL. Phần thú vị là nó sử dụng Delegate.Combine
để nối hai đại biểu.
.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
Tương tự như vậy, Delegate.Remove
được sử dụng trong remove_OnCall
.
Làm thế nào một sự kiện được gọi ra?
Để gọi OnCall
vào Do()
, nó chỉ cần gọi đại biểu được nối cuối cùng sau khi tải đối số:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
Chính xác thì người đăng ký tham gia một sự kiện như thế nào?
Và cuối cùng, Main
không ngạc nhiên, việc đăng ký OnCall
sự kiện được thực hiện bằng add_OnCall
phương thức gọi trên Foo
phiên bản.