【发布时间】:2021-03-14 16:00:44
【问题描述】:
我正在阅读 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 个事件,因为继承层次结构
【问题讨论】:
-
I don't why this approach is more efficient大概是因为没有 70 个字段,就像它说的那样。 -
哪本书?这听起来像是过早的优化。
-
我将引用的段落解释为
Control类为您提供了该集合。我不认为它建议你自己实现这样的集合。 -
the child instances will contain all the parent's delegate fields, so nothing you can save?你是怎么得出这个结论的? -
我好像记得你以前犯过这种误解:方法、属性和事件不会仅仅因为存在就耗尽每个对象的内存,它们每个类只使用一次内存。只有字段会增加每个对象的内存使用量,因此仅将已注册的事件存储在字典中会更有效