.NET 中的代码友好版本的事件签名

Posted

技术标签:

【中文标题】.NET 中的代码友好版本的事件签名【英文标题】:Code-friendly version of event signature in .NET 【发布时间】:2011-05-08 15:29:56 【问题描述】:

以前的帖子:

Event Signature in .NET — Using a Strong Typed 'Sender'?

In a C# event handler, why must the “sender” parameter be an object?


Microsoft 的约定和准则强制 .NET 用户使用特殊模式在 .NET 中创建、引发和处理事件。

活动设计指南http://msdn.microsoft.com/en-us/library/ms229011.aspx 声明


引用:

事件处理程序签名遵循以下约定:

返回类型为 Void。

第一个参数命名为sender 并且是对象类型。这是 引发事件的对象。

第二个参数名为 e 和 是 EventArgs 类型或派生的 EventArgs 的类。这是 特定于事件的数据。

该方法恰好需要两个 参数。


这些约定告诉开发人员(以下)更短且更明显的代码是邪恶的:

public delegate void ConnectionEventHandler(Server sender, Connection connection);

public partial class Server

    protected virtual void OnClientConnected(Connection connection)
    
        if (ClientConnected != null) ClientConnected(this, connection);
    

    public event ConnectionEventHandler ClientConnected;

并且(以下)更长且不太明显的代码很好:

public delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);

public class ConnectionEventArgs : EventArgs

    public Connection Connection  get; private set; 

    public ConnectionEventArgs(Connection connection)
    
        this.Connection = connection;
    


public partial class Server

    protected virtual void OnClientConnected(Connection connection)
    
        if (ClientConnected != null) ClientConnected(this, new ConnectionEventArgs(connection));
    

    public event ConnectionEventHandler ClientConnected;

尽管这些指南没有说明为什么遵循这些约定如此重要,但让开发人员表现得像猴子一样,不知道他们为什么要做什么。

恕我直言,Microsoft 的 .NET 事件签名约定对您的代码不利,因为它们会导致在编码、编码、编码上花费额外的零效率工作:

    编码“(MyObject)sender”转换(不是说 99% 的情况根本不需要发送者) 为要在事件处理程序内部传递的数据编码派生的“MyEventArgs”。 编码取消引用(在需要数据时调用“e.MyData”,而不仅仅是“数据”)

做到这一点并不难,但实际上,如果不遵守微软的惯例,我们会失去什么,除了人们认为你是异端,因为你的行为违反了微软的惯例verges on blasphemy :)

你同意吗?

【问题讨论】:

作为旁注,这条线是纯粹的邪恶:if (ClientConnected != null) ClientConnected(...);。你不应该,ever 调用这样的事件,因为它假定没有人会从另一个线程中删除事件处理程序。你冒着在这里扔 NRE 的风险。你应该改为:var h = ClientConnected; if (h != null) h(...);. 不幸的是,您的事件线程安全解决方案无法正常工作。请检查“The Wrong Solution #2, from the Framework Design Guidelines and MSDN”codeproject.com/Articles/37474/Threadsafe-Events.aspx(我无意让这个事件线程安全,这只是为了举例)但无论如何谢谢。 【参考方案1】:

关于有一个强类型的发件人,我自己也经常想知道。

关于 EventArgs,我仍然建议您使用中间 EventArgs 类,因为您可能希望在未来添加当前无法预见的事件信息。如果您一直使用特定的 EventArgs 类,您可以简单地更改类本身以及触发它的代码。如果您按照示例传递连接,则必须重构每个事件处理程序。

编辑

Jim Mischel 在他的 cmets 中提出了一个很好的观点。通过将发件人设为object,我们可以使相同的事件方法潜在地被重用于处理各种事件。例如,假设网格需要更新自身,如果:

用户单击“刷新”按钮,或 系统检测到从服务器加载了一个新条目。

你可以这样说:

serverBus.EntryReceived += RefreshNeededHandler;
refreshButton.Click += RefreshNeededHandler;

...
public void RefreshNeededHandler(object sender, EventArgs args) 

    ...

当然,在实践中,我几乎从未呼吁过这种重用,而在很多很多情况下,我倾向于将sender 转换为我知道的对象类型必须。如果我想重用这样的处理程序,我认为制作两个都调用相同便捷方法的处理程序会很容易。对我来说,事件处理程序在概念上应该处理特定对象组上的特定类型的事件。所以我个人并不认为object sender 方法是最好的约定。

但是,我可以想象这会非常方便的情况,例如,如果您想记录每个被触发的事件。

【讨论】:

将无法预见的信息放在哪里没有区别。 “Connection”和“EventArgs”之间的区别仅在于它们的名称。 @Lu4:如果 Connection 类仅用作事件参数,那可能是真的。但是,我感觉 Connection 对象的用途远不止于此。如果我是对的,您可能有一天想要添加与事件的实际触发相关的数据,但放在 Connection 对象上没有意义。 如果您选择强类型的发件人,那么您将限制可以生成事件的对象类型。例如,如果 UI 控件指定 SenderControl,则只有继承自 Control 的对象才能向 UI 控件发送事件。在我看来,即使对于 UI 控件的有限情况,这也是一个任意限制。 是的,我同意 Jim Mischel 的观点,所以这意味着当我们不限制发件人时,这允许更广泛地重用委托,而委托人有义务选择发件人。另一方面,绝对有限的发件人类型将导致为每个可能的发件人单独实现一个委托,即,我们将拥有 ButtonEventHandler、CheckBoxMouseEventHandler ......我们应该只计算哪种方法会导致更少的代码开发【参考方案2】:

你会遇到的问题:

    当您添加另一个参数时,您 将不得不改变你的活动 处理程序签名。

    程序员第一次看到 您的代码,您的事件处理程序将 看起来不像事件处理程序。

尤其是后者比编写一个 5 行的类会浪费你更多的时间。

【讨论】:

#2 是我的第一个想法:“没有人愿意和你一起玩(咳咳——'code with')” 1.数据在事件处理程序内部传递的方式没有区别,它是参数还是 MyEventArgs 类字段。 2. 这篇文章的目的是改变人们对事件处理程序的看法,看到这种新方法的开发人员会在前 5 秒被绊倒,但随后他们会意识到他们所经历的所有痛苦都是毫无意义的 啊,我不知道你的目标纯粹是政治性的。我以为你实际上是在问一个问题。【参考方案3】:

我发现不遵循约定的最大问题是,您会让习惯于以运行时库的方式处理事件的开发人员感到困惑。我不会说约定是好是坏,但它肯定不是邪恶。 .NET 开发人员知道并了解如何处理按照 Microsoft 指南编写的事件。在此基础上创建自己的事件处理机制可能在运行时更有效,甚至可能导致您认为更干净的代码。但它会有所不同,您最终会在程序中使用两个事件处理“标准”。

我的立场是,使用一个不太理想的标准(只要它没有被严重破坏)比使用两个相互竞争的标准要好。

【讨论】:

请描述一下当您看到以下方法时您会感到困惑的方式:private void server_ClientConnected(Server sender, Connection connection) ... @Lu4:他为什么要这么做?这件事你已经下定决心了,再解释一下又有什么用呢? @Lu4:当有不止一种方法可以做某事时,就会产生混乱总是。如果一个习惯于以 .NET 方式处理事件(即整个框架处理事件的方式)的 C# 程序员面对一个程序,其中一些事件以 .NET 方式处理而其他事件以其他方式处理,那么他必须记住哪种类型以哪种方式处理事件。但是,嘿......这是你的代码。你可以随心所欲地写它。 好吧,我仍然不同意你们,不是因为我愿意争论(我有更好的事情可以花时间),而是因为你的方法让我和其他人一个迷信的开发商。我同意拥有两个并行标准是不好的。但这种情况并非如此。因为选择事件或方法的签名是我们与生俱来的权利,所以那里不应该有一个标准。但在这样做之前,我们应该了解它会导致什么后果(我的意思是代码,而不是人)。如果我们使用非微软签名,我们的代码将如何变化。你对此的看法...【参考方案4】:

我使用了强类型事件(而不是对象,因为这样我就不必进行强制转换了),这真的不难理解,“哦,看他们使用了不是对象的类型”

对于 eventArgs,您应该使用它以防对象根据@StriplingWarrior 的回答发生变化。

我不明白为什么开发人员会对此感到困惑?

【讨论】:

...尤其是可以为您生成事件处理程序...无法想到我手动写出事件处理方法的时间。

以上是关于.NET 中的代码友好版本的事件签名的主要内容,如果未能解决你的问题,请参考以下文章

程序集强签名和安装程序数字签名

.NET:RS256 签名的 JWT 中的签名无效

问题事件名称CLR20r3 OS 版本6.1.7601.2.1.0.768.2 区域设置 ID 2052

微信签名算法的服务端实现(.net版本)

代码签名证书[关闭]

.NET COM 接口方法签名在 Windows 注册表中的位置?