c# - 为什么要明确实施事件?
问题描述
我正在通过 Jeffrey Richter 的 C# 阅读 CLR,它说:
public event EventHandler<NewMailEventArgs> NewMail;
当 C# 编译器编译上述代码行时,它会将这一行源代码转换为以下三个结构:
private EventHandler<NewMailEventArgs> NewMail = null;
// 2. A PUBLIC add_Xxx method (where Xxx is the Event name)
public void add_NewMail(EventHandler<NewMailEventArgs> value) {
... // use Delegate.Combine internally
}
// 3. A PUBLIC remove_Xxx method (where Xxx is the Event name) allows methods to unregister interest in the event.
public void remove_NewMail(EventHandler<NewMailEventArgs> value) {
... // use Delegate.Remove internally
}
作者说:
System.Windows.Forms.Control 类型定义了大约 70 个事件。如果 Control 类型通过允许编译器隐式生成 add 和 remove 访问器方法和委托字段来实现事件,那么每个 Control 对象将有 70 个委托字段,仅用于事件!因为大多数程序员只关心少数几个事件,所以从 Control 派生类型创建的每个对象都会浪费大量内存。为了有效地存储事件委托,每个公开事件的对象都将维护一个集合(通常是一个字典),其中某种事件标识符作为键,一个委托列表作为值。
因此,例如,我们应该在一个类型中显式地实现一个事件:
public sealed class EventKey { }
public sealed class EventSet {
private readonly Dictionary<EventKey, Delegate> m_events = new Dictionary<EventKey, Delegate>();
// Adds an EventKey -> Delegate mapping if it doesn't exist or combines a delegate to an existing EventKey
public void Add(EventKey eventKey, Delegate handler) {
...
}
// Removes a delegate from an EventKey (if it exists) and
// removes the EventKey -> Delegate mapping if the last delegate is removed
public void Remove(EventKey eventKey, Delegate handler) {
...
}
// Raises the event for the indicated EventKey
public void Raise(EventKey eventKey, Object sender, EventArgs e) {
... // use Delegate.DynamicInvoke internally
}
}
public class TypeWithLotsOfEvents {
private readonly EventSet m_eventSet = new EventSet();
protected static readonly EventKey s_fooEventKey = new EventKey();
public event EventHandler<FooEventArgs> Foo {
add { m_eventSet.Add(s_fooEventKey, value); }
remove { m_eventSet.Remove(s_fooEventKey, value); }
}
...
}
我不明白为什么这种方法效率更高,它仍然需要声明它包含的每个事件,并且对于 TypeWithLotsOfEvents 的派生类型,子实例将包含所有父的委托字段,所以你可以保存什么?以包含 70 个事件的 windows 窗体控件类型为例,任何派生的控件类型也必须包含 70 个事件,因为继承层次结构
解决方案
这里要注意的重要一点是,这...
public event EventHandler<FooEventArgs> Foo {
add { m_eventSet.Add(s_fooEventKey, value); }
remove { m_eventSet.Remove(s_fooEventKey, value); }
}
不生成任何字段。
这仅生成两种方法:
private void add_Foo(EventHandler<FooEventArgs> value) {
m_eventSet.Add(s_fooEventKey, value);
}
private void remove_Foo(EventHandler<FooEventArgs> value) {
m_eventSet.Remove(s_fooEventKey, value);
}
这与自动实现的属性如何生成支持字段以及 getter 和 setter 方法非常相似,而非自动实现的属性仅生成 getter 和 setter 方法。
请注意,您仍然拥有字段s_fooEventKey
,但该字段是静态的,因此只有一个实例,而不是每个实例的字段实例TypeWithLotsOfEvents
。所以我们都很好。
尝试使用反射打印出Control
. 您将看到它没有与其事件对应的字段。另一方面,如果您编写自己的类而不使用“事件字典”方法,并尝试打印出它的字段,您将看到您声明的每个事件都有一个字段。
推荐阅读
- python - 在一个 plot() 中绘制多条线时,Pyplot 返回不同的对象
- regex - 使用记事本++上的正则表达式提取以不同值重复的标签
- javascript - 追加限制为 1 时更新变量的值
- php - PHP - 对不同的数组求和
- tomcat - intellij 的 gwh 问题:无法反序列化响应
- c++ - 有没有办法在条件语句中声明对象?
- r - 多列时间序列分析
- angular - 如果我决定在没有 ngx-leaflet 库的情况下在 Angular 中使用传单,我需要照顾自己什么?
- python - 如何比较两个列表的效率
- html - Recharts - 将 dataKey 的值传递给子组件