在类中打包事件参数,为什么?

本文关键字:参数 为什么 事件 包事件 | 更新日期: 2023-09-27 18:15:57

大多数。net股票事件都有这样的签名:

delegate void SomethingSomething(SomethingEventArgs e);
event SomethingSomething OnSomethingSomething;

class SomethingEventArgs
{
    public string Name;
    public int Index;
    public double Groar;
}

为什么(显然是,否则任何人都会选择这样做)比:

delegate void SomethingSomething(string Name, int Index, double Groar);
event SomethingSomething OnSomethingSomething;

,因为您不必将参数打包到对象中,并且没有初始化式()。. NET 2.0)这是一种打字练习。

想到的一个原因是,当你把你的值打包在一个对象中时,你可以更简单地返回它们。处理程序可以修改对象的成员。然而,对于多播事件,这并不总是好的。

所以,为什么?

在类中打包事件参数,为什么?

阅读Open/Closed principle

通过使用类,所有继承的类都可以引入额外的功能,而不必更改委托签名。他们可以简单地引入一个继承您的(SomeEventArgs)的新事件类(ExtendedEventArgs)。

主要原因是它更易于维护。如果你传递一个对象,而其中任何属性发生了变化,你只需要修改它。如果传递变量,那就需要做更多的工作。因此,通过这种方式,代码变得更易于维护和更易读。

引用自Microsoft的Code Complete:

将例程的参数数量限制在7个左右。七是一人们理解的神奇数字。心理学研究表明发现人们一般记不住超过7个一次获得大量的信息(米勒1956)。这一发现应用于大量的学科,而且似乎很安全我猜想大多数人都记不住超过7个例程参数。

在实践中,你可以限制多少参数的数量取决于您的语言如何处理复杂数据类型。如果您使用支持结构化的现代语言进行编程数据时,可以传递包含13个字段和的复合数据类型把它想象成一个心理"块"数据。如果你在一个更原始语言,您可能需要单独传递所有13个字段,

如果你发现自己总是传递多个参数,例程之间的耦合过于紧密。设计例程或组例程以减少耦合。如果你也路过这里数据到许多不同的例程,将这些例程分组成一个类和将频繁使用的数据视为类数据。

引用原文

这样做的原因是为了避免破坏更改。例如,你的类可能希望在它的事件中包含进一步的信息,但是使用该事件的所有东西都会中断,因为委托不再匹配。通过使用严格的委托,您的事件可以在将来封装更多信息,而不会影响任何订阅者。

编辑:根据评论,我将扩展这如何影响破坏性更改的减少。

如果我们希望在我们的引发事件中添加更多的信息,通过使用从EventArgs派生的单个类,可以添加新的属性/方法。这意味着该事件的任何现有订阅者都不需要更改,因为添加这些属性不会影响它们。唯一需要改变的是在哪里设置/使用这些属性,例如在哪里引发事件。

好处是模式;拥有一个模式既可以提供一致性,又可以跨多个事件类型使用其他api:

  • EventHandler<T>委托类型(你不需要定义你自己的委托类型)
  • 响应式扩展(Rx)将事件转换为IObservable<T>,允许在事件源上使用LINQ和Observable.FromEvent

你的签名也错了:

  • 委托接受两个参数:object sourceSomethingEventArgs
  • SomethingEventArgs继承EventArgs
因此,您的代码应该是,作为模式的范例:

在命名空间范围:

public class SomethingEventArgs : EventArgs {
    public string Name;
    public int Index;
    public double Groar;
}
public delegate void SomethingSomething(object source, SomethingEventArgs e);

和暴露类型

的类型
public event SomethingSomething OnSomethingSomething;

(事件也可以是internal .)

正如其他人指出的那样,这是出于可维护性和一致性的原因。EventArgs方法还使事件处理程序修改 EventArgs成为可能。

修改EventArgs的一个原因是错误处理。在后台线程的某个地方捕获的异常作为事件传递给客户端。客户端可以在EventArgs中设置一个标志,表明异常已经处理,不应该在后台线程中重新抛出。

另一个例子是ObjectDataSource类,它允许客户端在需要时提供对象实例。这是通过订阅ObjectDataSource.ObjectCreating事件并通过设置EventArgs的成员来提供对象实例来完成的。

使用类允许事件的订阅者影响结果。

当您将类的实例作为参数传递给方法时,它是通过引用传递的。这允许在方法调用期间更改对象实例。事件引发后,调用者可以读取这些更改后的值。

例如:

查看FormClosingEventHandler委托(在Windows窗体中)。

这个委托使用一个FormClosingEventArgs类型的参数。

如果使用此委托的事件订阅者将Cancel属性(由CancelEventArgs继承)设置为true,则窗体不会关闭。

此外,以下答案也是正确的:

  1. jgauffin
  2. Baszz

首先,为什么这个模式如此普遍(正如你所问的)的原因是微软在开发事件模式时特别规定了(是的,他们也创造了这个术语)。我不一定认为这比自己设计委托签名好或坏,但是遵循一个众所周知的约定有它的优点。

每个微软:

  • 返回类型为Void

  • 第一个参数名为sender,类型为Object。触发事件的对象

  • 第二个参数名为e,类型为EventArgs或EventArgs的派生类。

  • 方法只接受两个参数

为了在你的问题上得到更多,虽然,我认为使用EventArgs的理由是双重的:

  1. 这样,从你的类继承的类仍然可以用派生的EventArgs类引发事件。
  2. 以便处理事件的可以使用公共事件处理程序来处理几种类型的事件,即使不同的事件使用不同的EventArgs。或者,如果您更改了事件,任何已经处理该事件的类都不需要更改代码,因为处理程序仍然与新事件兼容。

要进一步阅读,请参阅来自Microsoft的更多信息:

  1. 关于如何设计事件的更多信息:http://msdn.microsoft.com/en-us/library/ms229011.aspx
  2. 使用此模式的详细方法:http://msdn.microsoft.com/en-us/library/w369ty8x.aspx

为了遵循。net标准,推荐的创建事件的方法是:

  1. 创建一个继承自EventArgs的类
  2. 使用EventHandler<T>泛型委托

这样做减少了随后更改发送给事件订阅者的值的数量和类型所需的工作量。

的例子:

public event EventHandler<SometingEventArgs> SomethingEvent;
class SomethingEventArgs 
{     
      public string Name;    
      public int Index;     
      public double Groar; 
}

除了其他人已经提到的向后兼容性约定之外,EventArgs类还可以在将参数传递给多个对象时对如何连续访问参数进行更多控制。

这个控件可以用来使某些值不可变,这样如果一个事件被发送到3个不同的订阅者,你可以保证它不会被篡改。

同时,你可以提供多个[out]值,而不必同时处理返回值或[ref]参数,这将使组播过程变得非常复杂。它还确保任何验证逻辑都发生在EventArgs子类中,而不是在触发事件的类中。