【问题标题】:Action<T> vs delegate eventAction<T> 与委托事件
【发布时间】:2011-01-17 22:57:15
【问题描述】:

我已经看到开发人员使用以下代码完全不同。这些之间的确切区别是什么,哪些符合标准?它们是否相同,因为 ActionFunc&lt;T&gt; 也是代表:

public event Action<EmployeeEventAgs> OnLeave;
public void Leave()
{
    OnLeave(new EmployeeEventAgs(this.ID));
}

VS

public delegate void GoOnLeave(EmployeeEventAgs e);
public event GoOnLeave OnLeave;
public void Leave()
{
    OnLeave(new EmployeeEventAgs(this.ID));
}

【问题讨论】:

    标签: c# delegates


    【解决方案1】:

    Fwiw,这两个示例都没有使用标准的 .NET 约定。 EventHandler&lt;T&gt; 泛型应该声明事件:

    public event EventHandler<EmployeeEventArgs> Leave;
    

    应为引发事件的受保护方法保留“On”前缀:

    protected virtual void OnLeave(EmployeeEventArgs e) {
        var handler = Leave;
        if (handler != null) handler(this, e);
    }
    

    您没有必须这样做,但任何人都会立即识别该模式,理解您的代码并知道如何使用和自定义它。

    它有一个很大的优势,那就是不必在自定义委托声明和Action&lt;&gt; 之间进行选择,EventHandler&lt;&gt; 是最好的方法。哪个回答了你的问题。

    【讨论】:

    • 这是个好建议,但我认为它不能回答问题
    • 您是否有意将Leave 处理程序声明为委托,而不是:public event EventHandler ...?当您使用暴露事件的类时,event 关键字是一个相当大的游戏规则改变者,因为没有它,使用 obj.Leave = someHandler 而不是 obj.Leave += someHandler 可以简单地清除整个事件集合
    • 不,那是个错误。三年内没人注意到 :) 谢谢!
    【解决方案2】:

    以下两行代码几乎是等价的:

    public event Action<EmployeeEventAgs> Leave;
    

    相比:

    public event EventHandler<EmployeeEventAgs> Leave;
    

    区别在于事件处理方法的签名。如果您对动作使用第一种方法,您可以:

    public void LeaveHandler(EmployeeEventAgs e) { ... }
    

    然后是这个:

    obj.Leave += LeaveHandler;
    

    使用第二种方法,LeaveHandler 的签名需要不同:

    public void LeaveHandler(object sender, EmployeeEventAgs e) { ... }
    

    非常重要注意,在这两种情况下,event 关键字都用于声明事件成员。以这种方式声明的事件成员不仅仅是类的一个字段,尽管它看起来好像是。相反,编译器将其创建为 事件属性1。事件属性类似于常规属性,只是它们没有getset 访问器。编译器允许它们仅用于 +=-= 分配的左侧(添加或删除事件处理程序)。无法覆盖已分配的事件处理程序,或在声明它的类之外调用事件

    如果两个示例中缺少 event 关键字,您可以执行以下操作而不会出现错误或警告:

    obj.Leave = LeaveHandler;
    

    这将删除所有已注册的处理程序并将它们替换为LeaveHandler

    另外,你也可以执行这个调用:

    obj.Leave(new EmployeeEventAgs());
    

    如果您打算创建事件,则上述两种情况被视为反模式。事件应仅由所有者对象调用,并且不应允许无法追踪删除订阅者。 event 关键字是 .NET 的编程结构,可帮助您坚持正确使用事件。

    考虑到上述情况,我相信很多人都会坚持使用EventHandler 方法,因为如果没有event 关键字,则不太可能使用EventHandler。动作有更广泛的使用范围,它们在使用时看起来不像事件那样自然。当然,后者是个人意见,因为事件处理程序方法可能在我自己的编码实践中变得过于硬性。不过,如果动作使用得当,将它们用于事件并不是犯罪。


    1 事件属性是编译器在看到这样的代码时自动生成的:

    event EventHandler SomeEvent 
    

    变成如下代码大致相同:

    private EventHandler _someEvent; // notice the lack of the event keyword!
    public event EventHandler SomeEvent
    {
        add { _someEvent += value; }
        remove { _someEvent -= value; }
    }
    

    我们这样写的事件调用:

    this.SomeEvent(sender, args);
    

    转换成这个:

    this._someEvent(sender, args);
    

    【讨论】:

      【解决方案3】:

      Action&lt;T&gt;delegate void ... (T t) 完全相同

      Func&lt;T&gt;delegate T ... () 完全相同

      【讨论】:

      • 完全相同...它们确实具有相同的签名,但它们不兼容分配(恕我直言,这很不幸...)
      • 这是否使它们不完全相同? X 和 Y 在这里不同吗: public delegate void X(T t);公共代表 void Y(T t);您不能将一个分配给另一个。
      【解决方案4】:

      Action 只是完整委托声明的捷径。

      public delegate void Action<T>(T obj)
      

      http://msdn.microsoft.com/en-us/library/018hxwa8.aspx

      使用哪一种取决于您所在组织的编码标准/风格。

      【讨论】:

        【解决方案5】:

        You may want to look here,看看编译器实际为Action生成了什么是最好的描述。您编写的内容在功能上没有区别,只是语法更短、更方便。

        【讨论】:

          【解决方案6】:

          是的,Action 和 Func 只是在 3.5 clr 中定义的便利委托。

          Action、Func 和 lambdas 都只是语法糖和使用委托的便利。

          他们没有什么神奇之处。有几个人编写了简单的 2.0 插件库来将此功能添加到 2.0 代码中。

          【讨论】:

            【解决方案7】:

            一般来说,它们是等价的。但是在使用委托作为事件类型的上下文中,约定是使用 EventHandler(其中 T 继承了 EventArgs):

            public event EventHandler<EmployeeEventArgs> Left;
            
            public void Leave()
            {
                OnLeft(this.ID);
            }
            
            protected virtual void OnLeft(int id)
            {
                if (Left != null) {
                    Left(new EmployeeEventArgs(id));
                }
            }
            

            【讨论】:

              【解决方案8】:

              您可以自己编写这些 Action 和 Func 泛型委托,但由于它们通常很有用,因此他们为您编写了它们并将它们嵌入到 .Net 库中。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-09-09
                • 2017-07-02
                • 1970-01-01
                • 2011-05-27
                • 1970-01-01
                • 1970-01-01
                • 2011-06-24
                相关资源
                最近更新 更多