.NET中有一个用于事件的标准模式-它们使用delegate
类型,该类型接受一个称为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)类型,请不要选择启用了反差的类型。 最佳答案
没什么,你什么也不会损失。自从.NET 3.5发布以来,我一直在使用Action<>
,它更加自然,而且更易于编程。
我什至不再处理生成的事件处理程序的EventHandler
类型,只需编写所需的方法签名并用lambda进行连接:
btnCompleteOrder.OnClick += (o,e) => _presenter.CompleteOrder();
关于c# - 放弃.NET中的标准EventHandler模式,我将失去什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1120506/