代码之家  ›  专栏  ›  技术社区  ›  Boris Lipschitz

事件操作<>vs事件事件事件处理程序<>

c#
  •  121
  • Boris Lipschitz  · 技术社区  · 15 年前

    申报有什么不同吗 event Action<> event EventHandler<> .

    假设哪个对象实际引发了一个事件并不重要。

    例如:

    public event Action<bool, int, Blah> DiagnosticsEvent;
    

    VS

    public event EventHandler<DiagnosticsArgs> DiagnosticsEvent;
    
    class DiagnosticsArgs : EventArgs
    {
        public DiagnosticsArgs(bool b, int i, Blah bl)
        {...}
        ...
    }
    

    两种情况下的用法几乎相同:

    obj.DiagnosticsEvent += HandleDiagnosticsEvent;
    

    有几件事我不喜欢 事件事件处理程序 模式:

    • 派生自的额外类型声明 意外事件
    • 强制传递目标源 通常没人在乎

    更多的代码意味着需要维护更多的代码,而没有任何明显的优势。

    因此,我更喜欢 事件操作<gt;

    但是,只有当操作中的类型参数太多时,才需要额外的类。

    6 回复  |  直到 7 年前
        1
  •  63
  •   Fredrik Mörk    15 年前

    主要的区别是如果你使用 Action<> 您的事件实际上不会遵循系统中任何其他事件的设计模式,我认为这是一个缺点。

    一个优势与主导的设计模式(除了权力的相同性)是,你可以扩展 EventArgs 具有新属性而不更改事件签名的对象。如果你使用 Action<SomeClassWithProperties> 但是我不认为在这种情况下不使用常规方法有什么意义。

        2
  •  78
  •   Paul Rohde    13 年前

    根据前面的一些答案,我将把我的答案分为三个部分。

    第一,使用的物理限制 Action<T1, T2, T2... > vs使用派生类 EventArgs . 有三种方法:首先,如果更改参数的数量或类型,则必须更改订阅的每个方法以符合新模式。如果这是第三方程序集将要使用的面向公共的事件,并且事件参数可能会更改,则这将是使用从事件参数派生的自定义类以实现一致性的原因(请记住,您仍然可以使用 Action<MyCustomClass> 第二,使用 动作<T1、T2、T2…gt; 将阻止您将反馈传递回调用方法,除非您有与操作一起传递的某种对象(例如,具有已处理的属性)。第三,您不会得到命名参数,因此如果您要传递3 bool 是一个 int string S和A DateTime ,您不知道这些值的含义是什么。 作为附带说明,您仍然可以使用“在仍在使用时安全地激发此事件方法” 动作<T1、T2、T2…gt; “。

    第二,一致性含义。如果您有一个已经在使用的大型系统,那么遵循系统其余部分的设计方式几乎总是更好的,除非您有一个非常好的理由不这么做。如果您公开面对需要维护的事件,那么替换派生类的能力可能很重要。记住这一点。

    第三,在现实生活中,我个人发现,我倾向于为我需要与之交互的属性更改(尤其是在使用相互交互的视图模型执行MVVM时)或事件具有单个参数的情况下,创建许多一次性事件。大多数时候,这些事件的形式 public event Action<[classtype], bool> [PropertyName]Changed; public event Action SomethingHappened; . 在这些情况下,有两个好处。首先,我得到了发行类的类型。如果 MyClass 声明并且是唯一触发事件的类,我得到 类名 在事件处理程序中使用。其次,对于简单的事件(如属性更改事件),参数的含义是显而易见的,并以事件处理程序的名称表示,我不必为这些类型的事件创建大量的类。

        3
  •  15
  •   Marc Gravell    15 年前

    在大多数情况下,我会说遵循模式。我 偏离了它,但很少,而且是出于特定的原因。在这种情况下,我会遇到的最大问题是,我可能仍然会使用 Action<SomeObjectType> ,允许我以后添加额外的属性,并偶尔使用双向属性(想想 Handled 或用户需要的其他反馈事件 设置 事件对象上的属性)。一旦你开始走下这条线,你最好使用 EventHandler<T> 对于一些 T .

        4
  •  14
  •   Paul Matovich    13 年前

    当您的代码位于一个300000行的项目中时,更为复杂的方法的优势就出现了。

    用这个动作,就像你一样,没有办法告诉我布尔,int和blah是什么。如果您的操作传递了一个定义参数的对象,那么确定。

    使用一个需要EventArgs的事件处理程序,如果您要用getter为注释了其用途的属性完成诊断args示例,那么您的应用程序就更容易理解了。另外,请在diagnosticsargs构造函数中注释或完整命名参数。

        5
  •  6
  •   Paul Westcott    15 年前

    如果您遵循标准的事件模式,那么您可以添加一个扩展方法,以使事件触发检查更安全/更容易。(即,下面的代码添加了一个名为safefire()的扩展方法,该方法执行空检查,并且(显然)将事件复制到单独的变量中,以避免出现通常会影响事件的空竞争条件。)

    (尽管我有两种想法,您是否应该对空对象使用扩展方法…)

    public static class EventFirer
    {
        public static void SafeFire<TEventArgs>(this EventHandler<TEventArgs> theEvent, object obj, TEventArgs theEventArgs)
            where TEventArgs : EventArgs
        {
            if (theEvent != null)
                theEvent(obj, theEventArgs);
        }
    }
    
    class MyEventArgs : EventArgs
    {
        // Blah, blah, blah...
    }
    
    class UseSafeEventFirer
    {
        event EventHandler<MyEventArgs> MyEvent;
    
        void DemoSafeFire()
        {
            MyEvent.SafeFire(this, new MyEventArgs());
        }
    
        static void Main(string[] args)
        {
            var x = new UseSafeEventFirer();
    
            Console.WriteLine("Null:");
            x.DemoSafeFire();
    
            Console.WriteLine();
    
            x.MyEvent += delegate { Console.WriteLine("Hello, World!"); };
            Console.WriteLine("Not null:");
            x.DemoSafeFire();
        }
    }
    
        6
  •  2
  •   user1832484    7 年前

    看着 Standard .NET event patterns 我们发现

    .NET事件委托的标准签名是:

    void OnEventRaised(object sender, EventArgs args);

    […]

    参数列表包含两个参数: 发送者 和事件参数。发送方的编译时类型是System.Object,即使您可能知道 更多派生 总是正确的类型。按照惯例,使用 对象 .

    在下面的同一页中,我们发现了一个典型事件定义的示例,它类似于

    public event EventHandler<EventArgs> EventName;
    

    我们有没有定义

    class MyClass
    {
      public event Action<MyClass, EventArgs> EventName;
    }
    

    处理程序可能

    void OnEventRaised(MyClass sender, EventArgs args);
    

    哪里 sender 有正确的( 更多派生 )类型。