c# - 如果放弃 .NET 中的标准 EventHandler 模式,我会失去什么?

标签 c# events delegates binary-compatibility

.NET 中的事件有一个标准模式 - 它们使用 delegate type 接受一个名为 sender 的普通对象,然后是第二个参数中的实际“有效负载”,该参数应派生自 EventArgs .

第二个参数的基本原理来自 EventArgs看起来很清楚(参见 .NET Framework Standard Library Annotated Reference )。它旨在随着软件的发展确保事件接收器和源之间的二进制兼容性。对于每个事件,即使它只有一个参数,我们也会派生一个自定义事件参数类,该类具有包含该参数的单个属性,因此我们保留了在未来版本中向有效负载添加更多属性的能力,而不会破坏现有的客户端代码.在独立开发组件的生态系统中非常重要。

但我发现零参数也是如此。这意味着如果我的第一个版本中有一个没有参数的事件,我会写:

public event EventHandler Click;

...那我做错了。如果我将来将委托(delegate)类型更改为一个新类作为其有效负载:

public class ClickEventArgs : EventArgs { ...

... 我将破坏与我的客户的二进制兼容性。客户端最终绑定(bind)到内部方法的特定重载 add_Click这需要 EventHandler ,如果我更改委托(delegate)类型,那么他们就找不到该重载,所以有一个 MissingMethodException .

好吧,如果我使用方便的通用版本呢?

public EventHandler<EventArgs> Click;

不,还是错了,因为 EventHandler<ClickEventArgs>不是 EventHandler<EventArgs> .

所以为了获得EventArgs的好处,您必须从它派生,而不是直接按原样使用它。如果您不这样做,那么您还不如不使用它(在我看来)。

然后是第一个参数,sender .在我看来,这就像是邪恶耦合的秘诀。事件触发本质上是一个函数调用。一般而言,该函数是否应该能够通过堆栈回溯并找出调用者是谁,并相应地调整其行为?我们是否应该强制要求接口(interface)应该像这样?

public interface IFoo
{
    void Bar(object caller, int actualArg1, ...);
}

毕竟Bar的实现者可能想知道 caller 是谁是,所以他们可以查询更多信息!我希望你现在正在呕吐。为什么事件会有所不同?

所以即使我准备好忍受制作一个独立的痛苦EventArgs - 我声明的每个事件的派生类,只是为了让我在使用 EventArgs 时值得无论如何,我绝对更愿意放弃对象发送者参数。

Visual Studio 的自动完成功能似乎并不关心您为事件使用的委托(delegate) - 您可以键入 += [hit Space, Return] 它会为您编写一个处理程序方法来匹配它恰好是的任何委托(delegate)。

那么,如果偏离标准模式,我会失去什么值(value)?

作为附加问题,C#/CLR 4.0 是否会采取任何措施来改变这一点,也许是通过委托(delegate)中的逆变?我试图对此进行调查,但点击了 another problem .我最初将问题的这一方面包含在另一个问题中,但它在那里引起了困惑。把它分成三个问题似乎有点多......

更新:

事实证明,我想知道逆变对整个问题的影响是正确的!

作为noted elsewhere ,新的编译器规则在类型系统中留下了一个漏洞,该漏洞在运行时会爆炸。通过定义 EventHandler<T> 有效地堵住了这个洞不同于Action<T> .

所以对于事件,为了避免这种类型的漏洞,你不应该使用 Action<T> .这并不意味着您必须使用 EventHandler<TEventArgs> ;这只是意味着,如果您使用通用委托(delegate)类型,请不要选择启用了逆变的委托(delegate)类型。

最佳答案

没有,你没有失去任何东西。我一直在使用 Action<>自从 .NET 3.5 问世以来,它就更自然也更容易编程了。

我什至不处理 EventHandler不再为生成的事件处理程序输入类型,只需编写您想要的方法签名并用 lambda 连接起来:

btnCompleteOrder.OnClick += (o,e) => _presenter.CompleteOrder();

关于c# - 如果放弃 .NET 中的标准 EventHandler 模式,我会失去什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1120506/

相关文章:

c# - 如何获得 PR (Pull Request) 号

python - 从许多网页中获取数据的最佳方式(线程/事件驱动)

delegates - Kotlin var 惰性初始化

c# - 如何在 Windows Phone 上优化带有图像的长列表选择器的性能?

c# - 创建像 ASP.NET MVC 3 ViewBag 这样的类?

c# - 无法访问我的对象的成员

node.js - 如何模拟 irc 服务器断开连接来测试我的 nodejs irc 机器人?

c# - 鼠标进入/离开Form和Button子事件问题

c# - C++ 事件处理

ios - Swift 弱委托(delegate)