代码之家  ›  专栏  ›  技术社区  ›  Dan7

.NET:每次事件触发时是否都创建新的EventArgs是一种良好的实践?

  •  8
  • Dan7  · 技术社区  · 16 年前

    例如,我有一个基本事件发布方法:

        protected virtual OnSomeEvent(EventArgs e)
        {
            var handler = SomeEvent;
            if (handler != null)
            {
                handler(this, e);
                // handler(this, new EventArgs());// EDIT: Yes it should be
                                               // handler(this, e),
                                               // ignore this one :D
            }
        }
    

    对于重写的派生类 OnSomeEvent 并在触发时引发其他事件:

        protected override OnSomeEvent(EventArgs e)
        {
            base.OnSomeEvent(e);
    
            if (ExtendedEvent != null)
            {
                OnExtendedEvent(e);
            }
        }
    
        protected void OnExtendedEvent(EventArgs e)
        {
           // some stuff done
           // new information the ExtendedEventArgs object needs 
           //  is not available until this point
    
           ExtendedEvent(this, new ExtendedEventArgs(someStuff, someOtherStuff));
        }
    

    如果派生继续这样进行,它将为每一代需要派生类的派生类创建一个新的派生EventArgs。然而,它似乎是 EventArgs 在.NET框架上,不设计为可变的(没有setter),这会阻止对象保留单个EventArgs实例,并在执行过程中对其进行修改。

    所以每次这样的事件发生时,它都会重新分配所有相关的内存 意外事件 物体。在图形密集型应用中,事件每秒可触发几十次(例如 OnPaint 事件控制),这真的是一个好的实践吗?

    我应该做些改变吗 OnExtendedEvent() 并使 ExtendedEventArgs 可变的,所以下面是可能的?

        protected ExtendedEventArgs extendedArgs = ExtendedEventArgs.Empty;
        protected void OnExtendedEvent(EventArgs e)
        {
           // some stuff done
           // new information the ExtendedEventArgs object needs 
           //  is not available until this point
    
           extendedArgs.someProperty1 = someStuff;
           extendedArgs.someProperty2 = someOtherStuff;
    
           ExtendedEvent(this, extendedArgs);
        }
    

    编辑:修正了示例代码,现在应该更清楚了。

    3 回复  |  直到 16 年前
        1
  •  3
  •   aaronb    16 年前

    每次激发一个新的不可变对象时,我都会创建它,因为事件参数中有值。

    主要原因是如果在处理现有事件时再次激发新事件会发生什么?

    这可能发生在多线程应用程序中,但也可能发生在单个线程上,如下面的示例所示:

    第一个事件使用以下值激发:

    extendedArgs.someProperty1 = "Fire 1";
    extendedArgs.someProperty2 = "Fire 1 Other Stuff";
    

    然后,第一个事件处理程序以某种方式执行某些操作,从而使用以下参数再次激发事件:

    extendedArgs.someProperty1 = "Fire 2";
    extendedArgs.someProperty2 = "Fire 2 Other Stuff";
    

    所有的事件处理程序都是为第二个事件处理的,现在我们又开始为第一个事件处理其余的事件处理程序。

    现在,由于使用了相同的对象,因此第一个事件的所有事件处理程序现在都将“fire 2”作为它们的someproperty1,因为第二个事件重写了这些值。

    正如@nobugz所提到的,不要害怕制造短暂的垃圾。

        2
  •  5
  •   Ed Swangren    16 年前

    首先,如果您只是忽略了一个eventargs参数,为什么还要将它作为激发方法呢?这是真正的浪费,但是资源消耗比您的方法告诉调用方的谎言问题要小。只需通过传递参数,您的激发方法可能不具有创建EventArgs对象所需的相关信息:

    protected virtual OnSomeEvent(EventArgs e)
    {
        var handler = SomeEvent;
        if (handler != null)
        {
            handler(this, e);
        }
    }
    

    所以,既然我们已经知道了,如果您的eventargs对象没有有意义的信息来告诉您的订户,那么只需使用eventargs.empty,这就是它的用途。对于定制的Eventargs类,您可以遵循相同的模式,但老实说,您不必担心任何问题。在应用程序中,创建EventArgs对象永远不会成为瓶颈,如果是,则会出现设计问题。

        3
  •  1
  •   Sam Harwell    16 年前

    我对你的 OnExtendedEvent 代码-是否要将事件作为 SomeEvent ?

    当客户机添加事件处理程序时,他们希望能够在处理事件时删除事件处理程序,如下所示:

    someObject.SomeEvent += OnSomeEvent;
    // ...
    private void OnSomeEvent(object sender, EventArgs e)
    {
        someObject.SomeEvent -= OnSomeEvent;
    }
    

    如果您不遵循标准的调度实践,那么这段代码会给使用您的代码的人带来很大的惊喜。