事件惯例 - 我不明白

时间:2010-04-19 17:54:42

标签: c# events conventions

我的班级有一个事件:

public class WindowModel
{
    public delegate void WindowChangedHandler(object source, WindowTypeEventArgs e);
    public event WindowChangedHandler WindowChanged;  

    public void GotoWindow(WindowType windowType)
    {
        this.currentWindow = windowType;
        this.WindowChanged.Invoke(this, new WindowTypeEventArgs(windowType));
    }
}

派生事件类:

public class WindowTypeEventArgs : EventArgs
{
    public readonly WindowType windowType;

    public WindowTypeEventArgs(WindowType windowType)
    {
        this.windowType = windowType;
    }
}

将其注册到活动的其他一些类:

private void SetupEvents()
{
   this.WindowModel.WindowChanged += this.ChangeWindow;
}

private void ChangeWindow(object sender, WindowTypeEventArgs e)
{
   //change window
}

遵循.Net约定我获得了什么? 拥有这样的合同会更有意义

public delegate void WindowChangedHandler(WindowType windowType);
public event WindowChangedHandler WindowChanged;

这样做,我不需要创建一个新类,更容易理解。 我不是在编写.Net库。此代码仅用于此项目。我喜欢惯例,但是当我说在这个例子中它没有意义或者我没有理解某些东西时我是对的吗?

3 个答案:

答案 0 :(得分:15)

孤立地看,是的,你是对的:.NET传统语法更冗长,更不直观,但有一些优点:

  • 事件传递的信息的未来更改不会自动要求更改事件的每个使用者。例如,如果您想为事件添加额外的信息 - 例如,WindowTitle字符串 - 您将不得不修改附加到该事件的每个函数的签名,而不管他们是否使用它。使用EventArgs方法,您可以将属性添加到参数中,并仅更改需要利用其他信息的函数。
  • 由于.NET 2.0引入了EventHandler<TEventArgs>委托类型,因此您不再需要手动定义自己的事件委托。在您的示例中,您可以将事件键入EventHandler<WindowTypeEventArgs>而不是WindowChangedHandler
  • EventArgs方法可以轻松地将多种类型的信息传递回调用函数。如果您需要在替代示例(直接传递event参数)中执行此操作,您仍然最终会创建自己的-tuple类来保存信息。

当您在创建实际执行调用的protected virtual函数时查看.NET事件的实际模式时,第一个的影响更明显。例如:

public event EventHandler<WindowTypeEventArgs> WindowChanged;

protected virtual void OnWindowChanged(WindowTypeEventArgs e)
{
    var evt = WindowChanged;

    if(evt != null) evt(this, e);
}

我想在此指出几件事:

  1. 使用创建此事件调用方法的模式可以避免在整个代码中进行空值检查(没有附加任何函数的事件将为null,并且如果您尝试调用它将抛出异常)
  2. 此模式还允许从您继承的类控制调用顺序,允许它们在任何外部使用者之前或之后显式执行其代码
  3. 这在多线程环境中尤为重要。如果你刚才说if(WindowChanged != null) WindowChanged(this, e);,那么在你检查它和你调用它之间,实际上会冒WindowChanged事件成为null的风险。这在单线程场景中并不重要,但是形成的防守习惯非常好。

答案 1 :(得分:3)

我认识到你的困惑!当我第一次看到这个时,我也有同感。

要意识到的重要一点是,它不会以编程方式为您提供很多优势,但它是一种在框架中众所周知的惯例。因此,有很多工具需要void EventName(object sender, EventArgs e)签名。例如,某些IoC容器可以使用此签名在构建时自动连接事件。

简而言之,它看起来有点奇怪,但这是一个惯例。坚持下去,灯泡最终会点亮!

答案 2 :(得分:1)

您可以使用您的代理人。没有人会强迫你。这只是一个很好的活动模式。

如果你使用标准的Sender-EventArgs模式,你也可以对其他事件使用相同的ChangeWindow处理程序。