为什么MSDN建议在委托声明中包含对象发送者?

时间:2011-09-25 18:03:11

标签: c# events delegates

我正在阅读this页面,我注意到它是如何说这是标准指南:

  

.NET Framework指南指出用于事件的委托类型应该使用两个参数,一个“对象源”参数指示事件的来源,一个“e”参数封装有关该事件的任何其他信息。

我可以理解object sender在某些情况下如何有用,但我可以看到其他情况完全相反。例如,

  1. 如果处理该事件的班级不应该知道是谁解雇了该怎么办?耦合,凝聚力以及所有这些。

  2. 就我而言,我已经将对象引用为成员变量。这就是我订阅活动的方式。它只会有一个实例,因此没有理由转换发送者对象而不仅仅是使用成员变量。

  3. 在我的程序中,发件人对象根本不应该为客户端所知。很难解释我在做什么,但基本上我在一个库中有一个内部构造函数的类,该库也被该库中的另外两个类使用。我的客户端类正在订阅这两个类中的事件,但事件最初是从客户端不应该知道的内部类调用的。

  4. 令事件处理程序的客户端感到困惑。库应该易于理解,在我的情况下,没有理由使用sender变量。没有。那为什么要包括它呢?

  5. 话虽如此,为什么微软表示事件处理程序应遵循这些指导原则?不是不总是最好的选择吗?

    编辑:感谢大家的回复。我决定选择大多数人,并使用EventHandler<T>来处理此库中的所有活动。

7 个答案:

答案 0 :(得分:1)

我认为该模式的原因是强制执行一些一致性。 sender参数允许为多个发布者(按钮,表)重用单个处理程序。

解决您的观点:

1)根本不使用它。这很常见,并没有真正伤害任何良好的做法。

2)没关系,再次忽略发件人

3)完全与你所说的2)... ...相矛盾 其余的与1)相同。您甚至可以考虑将null作为发件人传递。

4)“然后为什么要包括它” - 还有其他用例需要发件人。


但请注意,这只是图书馆确认BCL的指南 您的案例听起来更像是一个特定的应用程序(而不是库),所以请随意使用您喜欢的任何参数方案。编译器不会抱怨。

答案 1 :(得分:1)

你正在逆风而战,.NET框架有一定的设计,规则和指导方针,在使用它时,如果你想正确使用它,你应该遵循这些指示。

如果您使用原始代表,则可以获得所需的所有自由,但如上所述,如果要为事件声明委托类型,则还应包括sender对象和EventArgs对象(base或派生类)。

如果你违反这些规则,就像我刚才在回答你的另一个问题时所说的那样:Should I use EventArgs or a simple data type?,你可能最终会遇到代码中断的情况。

最大限度地简化,当框架在控件上调用OnClick事件时,.NET Framework确实传递了发件人和EventArgs实例......如果事件不符合,则可能会出现问题。< / p>

如果你想要完全自由,那么使用简单的代表而不是事件。

答案 2 :(得分:1)

首先,重要的是要注意指南不是法律。

如果你不遵守指导方针,那么所有的地狱(或类似的程序员)将打破。

因此,请随意更改事件的签名。

然而,知道为什么这些指南一开始就添加起来同样重要,而且该问题的答案的一大部分是版本控制。

通过以下两部分,这两部分:

  1. 事件的来源,尽可能输入“通用”(请注意,事件签名是在将适当的泛型引入系统之前设计的,所以object与后面的EventArgs一样通用)< / LI>
  2. 继承自sender
  3. 的对象

    然后您正在设计更适应变化的代码。

    首先,由于您未“允许”添加或删除参数,因此您活动的所有未来版本仍然只有ee

    其次,关于e参数的指南的第二部分。如果您在新版本的类库中决定通过更改Action参数的类型来更改事件处理程序的签名,那么您应该通过降序使其更具体 从你当前的类型,并传递后代。

    原因是已经处理了当前(旧)类型的现有代码仍然有效。

    因此指南背后的全部推理是:

    1. 保持一致(正如其他人所说)
    2. 为未来而设计(通过确保在发布版本X + 1时,对您的类的X版本编写的代码仍然有效,而无需手动更改该代码)
    3. 现在,如果您对此案件不感兴趣,请随时不遵守指南。

      事实上,你可以用{{1}}制作一个活动,它会正常工作。

答案 3 :(得分:1)

  

为什么呢?人们总是这样问。在这方面,这只是一个   图案。通过在类中打包事件参数,您会变得更好   版本化语义。通过具有共同模式(sender, e)   很容易学会作为所有事件的签名。我想回过头来   不好用Win32-当数据在WPARAM和LPARAM时,和   等等。该模式成为噪音,开发人员只是假设该事件   处理程序具有事件的发送者和参数的范围。

-Chris Anderson,Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries

如果您是.NET开发人员并且还没有阅读过该书,那么您就错过了。它为您提供了一个窗口;)进入Microsoft .NET Framework设计人员的思想,以及很多最佳实践(包括其背后的推理)。

(另外,您可以运行FxCop来验证是否遵循了这些做法。)

答案 4 :(得分:0)

此类指南允许事件消费者的可预测性。它还允许处理您在创建事件时可能从未考虑过的其他方案,特别是如果您的库由第三方开发人员使用。

它允许处理事件的方法始终具有正确的对象实例以及有关事件被触发的基本信息。

答案 5 :(得分:0)

这只是一种很好的做法,但只要您不需要了解触发事件的对象或与该对象相关的任何其他信息,您就可以了。我总是把它包括在内,因为你永远都不知道你什么时候需要它。

我的建议是坚持下去,它根本没有伤害。

答案 6 :(得分:0)

从语义上来说,让事件处理程序对事件的来源感兴趣使用包含这样一个字段的EventArgs的派生词是没有错的。实际上,在很多情况下,将Sender作为一个单独的参数传递比较清晰(例如,如果键盘快捷键处理器需要为按钮触发Click处理程序,事件不应该真的被认为是由按钮提出,而是代表按钮提出)。不幸的是,将这些信息合并到EventArgs派生类型中会使每次引发事件时都需要创建EventArgs派生类型的新实例,即使它可能会使用{{1 }}。使用单独的参数传递信息消除了在这种常见情况下让每个事件都创建新对象实例的需要。

虽然可能有关心事件来自何处的处理程序可以使用参数,并且那些不关心它的处理程序会省略该参数,这将需要任何帮助方法和类协助处理事件订阅需要具有包含参数的事件的版本。让所有事件都有两个参数,其中一个是EventArgs.Empty类型,其中一个是从Object派生的类型,这使得一个辅助方法或类能够处理所有事件。