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

时间:2011-11-25 14:30:17

标签: c# .net events

大多数.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),所以它就是一种打字练习。

我想到的一个原因是,当你将它们打包在一个对象中时,你可以更简单地返回你的值 - 即。 handler可以修改对象的成员。但是,对于多播事件,无论如何都不一定好。

那么,为什么?

9 个答案:

答案 0 :(得分:30)

了解Open/Closed principle

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

答案 1 :(得分:15)

主要原因是它更易于维护。如果传递对象并且任何属性发生更改,则只需修改它。如果你传递变量,那就更多了。因此,代码以这种方式变得更易于维护和更具可读性。

来自微软的Code Complete

的引用
  

将例程参数的数量限制为大约7。七是一个   人们理解的神奇数字。心理学研究有   发现人们一般不能追踪超过七个   大量的信息(米勒1956年)。这个发现一直都是   应用于大量的学科,似乎是安全的   猜想大多数人无法跟踪超过七个   常规参数一次。

     

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

     

如果你发现自己不断传递一些论点,   你的日常工作之间的联系太紧张了。设计例程或   减少耦合的一组例程。 1f你传递的是同样的   数据到许多不同的例程,将例程分组成一个类和   将常用数据视为类数据。

Quoted text from the original post's image

答案 2 :(得分:11)

这样做的原因是为了避免破坏变化。例如,您的班级可能希望在其事件中包含更多信息,但是当代理不再匹配时,使用该事件的每个事物都会中断。通过拥有严格的委托,您的事件可以在将来封装更多信息,而不会影响任何订阅者。

编辑:根据评论,我将扩展它如何影响减少破坏性变化。

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

答案 3 :(得分:9)

好处是模式;并且具有模式既提供了一致性,又提供了跨多种事件类型使用其他API的能力:

  • EventHandler<T>委托类型(您无需定义自己的委托类型)。
  • Reactive Extensions(Rx)将事件转换为IObservable<T>,允许在Observable.FromEvent的事件源上使用LINQ。

你的签名也错了:

  • 委托使用两个参数: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。)

答案 4 :(得分:6)

正如其他人所指出的那样,存在可维护性和一致性的原因。 EventArgs方法还使事件处理程序能够修改 EventArgs。

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

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

答案 5 :(得分:3)

使用类允许事件的订阅者实现结果。

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

例如:

查看FormClosingEventHandler Delegate(在Windows窗体中)。

此委托使用FormClosingEventArgs类型的参数。

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

此外,这些答案也是正确的:

  1. jgauffin
  2. Baszz

答案 6 :(得分:3)

首先,为什么这种模式如此普遍(正如您所问)的原因是因为 Microsoft已明确规定所以当他们开发 {{3 }} (是的,他们也创造了这个词)。我不一定认为这比提出自己的代表签名更好或更差,但遵循一个众所周知的约定可以有其优势。

Per Microsoft:

  
      
  • 返回类型为Void。

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

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

  •   
  • 该方法只需要两个参数。

  •   

尽管如此,为了获得更多问题,我认为使用EventArgs的理由是双重的:

  1. 因此,继承自您的类的类仍然可以使用派生的EventArgs类引发事件。
  2. 因此,处理事件的可以使用公共事件处理程序来处理多种类型的事件,即使不同的事件使用不同的EventArgs也是如此。或者,如果您在路上更改事件,则已经处理该事件的任何类都不需要更改其代码,因为处理程序仍将与新事件兼容。
  3. 有关详细信息,请参阅Microsoft的更多信息:

    1. 有关如何设计活动的更多信息:Event Pattern
    2. 详细使用此模式的方法:http://msdn.microsoft.com/en-us/library/ms229011.aspx

答案 7 :(得分:1)

为了遵循.net standards,创建事件的推荐方法是:

  1. 创建一个继承自EventArgs
  2. 的类
  3. 使用EventHandler<T>通用代理
  4. 这样做会减少随后更改发送给活动订阅者的值的数量和类型所需的工作量。

    示例:

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

答案 8 :(得分:1)

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

此控件可用于使某些值不可变,因此,如果将事件发送给3个不同的订阅者,则可以保证它不会被篡改。

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