.NET: Является ли хорошей практикой создание новых EventArgs каждый раз, когда событие запускается?

Например, у меня есть базовый метод публикации событий:

    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 не предназначены для изменения (без сеттеров), это препятствует тому, чтобы объект сохранял один экземпляр EventArgs и модифицировал его по ходу дела.

Таким образом, каждый раз, когда возникает подобное событие, оно будет перераспределять память для всех задействованных 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);
    }

РЕДАКТИРОВАТЬ: исправлен код примера, теперь должно быть понятнее.


person Dan7    schedule 29.01.2010    source источник
comment
Наследование не является проблемой, производный класс может вызывать только base.OnExtendedEvent с EventArgs.Empty. В общем, не бойтесь создавать недолговечный мусор.   -  person Hans Passant    schedule 29.01.2010
comment
Я думаю, вы имеете в виду неизменяемый вместо изменчивого. Неизменный означает, что вы не можете его изменить.   -  person aaronb    schedule 29.01.2010


Ответы (3)


Я бы создавал новый неизменяемый объект каждый раз, когда он запускается, так как в аргументах события есть значения.

Основная причина заключается в том, что произойдет, если новое событие будет запущено снова во время обработки существующего события?

Это может произойти в многопоточных приложениях, но может произойти даже в одном потоке, как показано в следующем примере:

Первое событие запускается со следующими значениями:

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

Затем каким-то образом первый обработчик событий делает что-то, вызывая повторный запуск события со следующими аргументами:

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

Все обработчики событий для второго события обработаны, и теперь мы возвращаемся к обработке остальных обработчиков событий для первого события.

Теперь, поскольку используется один и тот же объект, все обработчики событий для первого события теперь будут иметь «Fire 2» в качестве свойства someProperty1, так как второе событие перезаписало значения.

Как уже упоминал @nobugz, не бойтесь создавать недолговечный мусор.

person aaronb    schedule 29.01.2010

Во-первых, зачем использовать аргумент EventArgs для вашего метода запуска, если вы все равно просто игнорируете его? Это настоящая растрата, но потребление ресурсов менее проблематично, чем ложь, которую ваш метод говорит своим вызывающим. Просто передайте аргумент, ваш метод запуска, скорее всего, не будет иметь соответствующей информации, доступной для создания объекта EventArgs:

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

Итак, теперь, когда у нас есть это прямо, если ваш объект EventArgs не имеет значимой информации, чтобы сообщить вашим подписчикам, просто используйте EventArgs.Empty, для чего он существует. Вы можете следовать тому же шаблону для своих пользовательских классов EventArgs, но, честно говоря, вы ни о чем не беспокоитесь. Создание объектов EventArgs никогда не станет узким местом в вашем приложении, а если это так, то у вас возникнут проблемы с дизайном.

person Ed S.    schedule 29.01.2010

Меня немного смущает ваш код OnExtendedEvent — вы хотите перенаправить событие как SomeEvent?

Когда клиент добавляет обработчик событий, он ожидает, что сможет удалить обработчик событий во время обработки события, например:

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

Если вы не будете следовать стандартной практике диспетчеризации, этот код выдаст исключение, к большому удивлению человека, использующего ваш код.

person Sam Harwell    schedule 29.01.2010