【问题标题】:Events convention - I don't get it活动大会 - 我不明白
【发布时间】:2011-02-09 19:27:28
【问题描述】:

我的班级有一个活动:

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 库。此代码仅用于本项目。我喜欢约定,但是当我说在这个例子中它没有意义或者我误解了什么时,我说得对吗?

【问题讨论】:

    标签: c# events conventions


    【解决方案1】:

    您可以使用您的委托。没有人会强迫你。这只是一个很好的事件模式。

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

    【讨论】:

      【解决方案2】:

      我知道你的困惑!当我第一次看到这个时,我也有同样的感觉。

      要意识到的一件大事是,从编程上讲,它并没有给您带来太多优势,但它是框架中众所周知的约定。因此,有很多工具需要void EventName(object sender, EventArgs e) 签名。例如,一些 IoC 容器可以使用此签名在构建时自动连接事件。

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

      【讨论】:

        【解决方案3】:

        单独来看,是的,你是对的:.NET 常规语法更冗长且不太直观,但也有优势:

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

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

        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 的风险。这在单线程场景中并不重要,但可以养成良好的防御习惯。

        【讨论】:

        • 我真的很喜欢这个答案!第一点很明显,但我没有想到。第二点我不知道,但很好:) 不确定我是否理解你的第三点。谢谢!
        • @bobjink:很高兴它有帮助!我的第三点是关于能够将数据发送回调用事件的对象。使用EventArgs 方法,您可以允许事件的使用者修改其上的属性,例如CancelEventArgs 类,这将允许它将数据传回给您。
        猜你喜欢
        • 2015-04-23
        • 2022-11-16
        • 1970-01-01
        • 2021-05-13
        • 2011-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-20
        相关资源
        最近更新 更多