为啥事件不支持绑定继承类型?

Posted

技术标签:

【中文标题】为啥事件不支持绑定继承类型?【英文标题】:Why events does not support binding inherited types?为什么事件不支持绑定继承类型? 【发布时间】:2011-07-21 12:19:57 【问题描述】:

在这些代表中:

EventHandler

public delegate void EventHandler(object sender, EventArgs e);

FormClosingEventHandler

public delegate void FormClosingEventHandler(object sender, FormClosingEventArgs e);

FormClosingEventArgs 继承自 EventArgs。为什么我不能将 FormClosing 事件与来自 EventHandler 委托的事件处理程序绑定?

我知道事件处理程序的签名必须与其委托匹配,但为什么它不支持匹配继承的类型?

【问题讨论】:

【参考方案1】:

嗯,这很有趣……

可以使用兼容类型的方法组转换绑定事件处理程序:

public void GenericHandlerMethod(object sender, EventArgs e) 

...
// Valid
foo.FormClosingEvent += GenericHandlerMethod;

这实际上会创建一个FormClosingEventHandler 的实例,不是 EventHandler

但是您不能直接使用 EventHandler 类型的现有委托进行订阅:

EventHandler genericHandler = GenericHandlerMethod;
// Invalid
foo.FormClosingEvent += genericHandler;

...但如果类型兼容,您可以基于现有委托创建新委托:

EventHandler generic = GenericHandlerMethod;
FormClosingEventHandler closingHandler = new FormClosingEventHandler(generic);
// Valid
foo.FormClosingEvent += closingHandler;

基本上你需要记住,所有的语法糖都是有效地调用这样的方法:

foo.AddFormClosingHandler(handler);

方法的签名为:

public void AddFormClosingHandler(FormClosingHandler handler)

现在请记住,尽管它们具有兼容的签名,但没有从 EventHandlerFormClosingHandler 的参考转换可用。这不像是一个继承另一个。

它与泛型协变/逆变更加混淆,但我们暂时将其留在那里......希望这能给你一些东西来咀嚼和解决这些限制的选项。

【讨论】:

非常好的答案,谢谢。唯一的问题是在 UI 中生成事件处理程序的 IDE 不支持这样的工作。所以我必须手动绑定它们。为此我问逻辑上为什么它不支持。 @John:呸,当涉及到事件处理程序时,我讨厌 Visual Studio。希望答案能解释为什么在实际代码中存在限制 - VS 限制超出了我的理解;)【参考方案2】:

直接指向对象方法的委托包含三个信息:

    方法应作用的目标对象,或者在静态方法的情况下为“null” 对作用于该类型对象的函数的引用,如果目标为“null”,则为静态函数。 委托本身的类型。

通过使用Delegate.Combine 组合总共 N 个单播委托生成的委托将包含 N 个目标,N方法和 ONE 委托类型。鉴于 Delegate.CombineDelegate.Remove 的使用方式(恕我直言,相当恶心和不幸),系统不可能允许现有的 Combine 使用接受不同类型的代表。

例如,例程可能需要EventHandler<IFoo>。如果类MoeLarry 都实现IFooIBar,那么这样的例程应该能够接受EventHandler<Moe>EventHandler<Larry>。如果每个委托类型都有自己的Combine 定义,则可以为EventHandler<IFoo>.Combine() 提供EventHandler<Moe> 类型的委托和EventHandler<Larry> 之一,并让它生成EventHandler<IFoo> 类型的组合委托处理程序。不幸的是,所有委托类型都有一个 Delegate.Combine() 方法,它无法查看 EventHandler<Moe>EventHandler<Larry> 并确定组合委托应该是什么类型(即使 Delegate.Combine 有能力要识别两个事件处理程序都可以转换为的类型,它无法知道是使用EventHandler<IFoo> 还是EventHandler<IBar>)。

【讨论】:

以上是关于为啥事件不支持绑定继承类型?的主要内容,如果未能解决你的问题,请参考以下文章

为啥结构不支持继承?

为啥大多数编程语言不支持多重继承?

如果 JSF 不支持,为啥我可以将 <f:actionListener> 绑定到任意方法?

on绑定事件支持的事件类型

JavaScript之元素绑定事件

为啥jquery的ajax事件不支持谷歌浏览器