在没有委托变量支持的情况下使用事件有利于哪些情况?

我正在阅读Jon Skeet撰写的这篇文章, 这是我深入了解代表和事件的一部分。

在文章中,他演示了一个没有委托变量支持的事件,并声明……

…有时您不希望使用简单的委托变量来支持事件。 例如,在有大量事件但只有少数事件可能被订阅的情况下,您可以从描述事件的某个键到当前处理它的委托的映射。 这就是Windows Forms所做的 – 它意味着你可以拥有大量的事件,而不会浪费大量内存和变量,这些变量通常只有空值。

我不完全明白他在说什么。 有人可以充实这些例子吗? 例如,他的意思是“从描述事件的某个键映射到当前处理它的代理”? Windows Forms如何做到这一点?

谢谢!

您可以自己使用相同的类型 – EventHandlerList 。 假设您有100个事件 – 这通常意味着有100个变量,即使没有人订阅过该事件也会占用空间。 而不是这样, EventHandlerList 有点Dictionary – 它只在您第一次订阅特定事件时在其内部数据结构中创建一个条目。

所以你可能有类似的东西:

 // Actual values don't matter; they're just keys private const string FirstEventKey = "FirstEvent"; private const string SecondEventKey = "SecondEvent"; private readonly EventHandlerList events = new EventHandlerList(); public event EventHandler FirstEvent { add { events.AddHandler(FirstEventKey, value); } remove { events.RemoveHandler(FirstEventKey, value); } } public event EventHandler SecondEvent { add { events.AddHandler(SecondEventKey, value); } remove { events.RemoveHandler(SecondEventKey, value); } } public void OnFirstEvent(EventArgs e) { EventHandler handler = (EventHandler) events[FirstEventKey]; if (handler != null) { handler(this, e); } } // Similarly for OnSecondEvent 

我想补充一点,这是我经常看到的

 // bad code class MyControl : Control { public event EventHandler ValueChanged; private CheckBox checked; // ... private void InitializeComponent() { // ... checked.CheckedChanged += checked_CheckedChanged; // ... } private void checked_CheckedChanged(object sender, EventArgs e) { if (ValueChanged != null) { ValueChanged(sender, e); } } } 

我认为这是一种反模式,因为这种方式更快,内存更少,总体上更简单我认为:

 class MyControl : Control { public event EventHandler ValueChanged { add { checked.CheckChanged += value; } remove { checked.CheckChanged -= value; } } private CheckBox checked; // ... } 

另一种情况是微不足道的情况,其中基类或接口为某些派生类型中永远不会发生的事件提供事件。 例如,只读的observable-collection接口可能提供CollectionChangedEvent。 持有接口变量的实体将无法使用它来更改集合,但可能有兴趣知道是否/何时更改了集合。 这样的实体应该能够使用不可变集合以及可变集合。 从它的角度来看,一个不可变的集合应该就像一个可变的集合,没有人在它正在观看时烦恼变异。

CollectionChangedEvent最合乎逻辑的实现是使用add-and -hand-handler方法,什么都不做,没有支持委托字段。 调用add-handler方法的外部实体实际上是在“如果此集合发生更改时给我打电话”。 当它调用remove-handler方法时,它基本上是在说“我不再需要知道这个集合是否会改变”。 如果集合永远不会改变,那么这些请求可以通过简单地什么都不做来兑现。