C#中事件的内部设计
本文关键字:内部 事件 | 更新日期: 2023-09-27 18:29:48
刚刚读完Jon Skeet关于活动和代表的文章,收到了一个问题。
让我们先说在代码中我声明一个事件
public event EventHandler MyEvent
然后我想以的方式在代码中提出它
if (MyEvent != null)
Myevent(this,EvtArgs.Empty);
Jon说事实上MyEvent看起来是这样的:
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add
{
lock (this)
{
_myEvent += value;
}
}
remove
{
lock (this)
{
_myEvent -= value;
}
}
}
问题是,当我比较MyEvent != null
时,实际会发生什么?事实上,据我所知,它将_myEvent
与null
进行了比较,但我不确定。
如果实现自定义的添加/删除访问器,则首先无法将MyEvent
与null进行比较,因为它将仅是一个事件-它没有这样的值,仅添加/删除访问者。您将不得不使用声明的字段(上面示例中的_myEvent
)。
当您使用类似事件的字段时,您只能使用事件名称进行比较,在该字段中,您(实际上)得到了一个字段和一个具有相同名称的事件。(编译后的代码实际上不必为字段名重复使用事件名,但它必须像编译时那样看起来。)
请注意,使用此:
if (MyEvent != null)
MyEvent(this,EvtArgs.Empty);
不是线程安全的,因为MyEvent
可能在检查和调用之间变成null。您应该使用:
EventHandler handler = MyEvent;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
还要注意,关于this
上类似字段的事件锁定的部分现在稍微过时了;在C#4中,使用无锁比较交换机制来实现线程安全。
当没有处理程序订阅MyEvent时,MyEvent将等于null。因此,在触发事件之前,首先要检查是否至少有一个处理程序订阅了它
在内部,事件是一组添加/删除方法,其支持字段类型为MulticastDelegate。当您在类外请求MyEvent并将此实例与null进行比较时,会得到此字段。
另请参阅这篇文章:C#事件是如何在幕后工作的?
编辑:此外,正如Jon已经指出的,如果您自己提供添加/删除方法,则不会得到字段。