Что-то не так с кратковременными обработчиками событий? - PullRequest
5 голосов
/ 27 сентября 2011

Например, что-то не так с этим?

private void button1_Click(object sender, EventArgs e)
{
    Packet packet = new Packet();

    packet.DataNotSent += packet_DataNotSent;

    packet.Send();
}

private void packet_DataNotSent(object sender, EventArgs e)
{
    Console.WriteLine("Packet wasn't sent.");
}

class Packet
{
    public event EventHandler DataNotSent;

    public void Send()
    {
        if (DataNotSent != null)
        {
            DataNotSent(null, EventArgs.Empty);
        }
    }
}

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

Это просто кажется ... странным. Не уверен, правильно это или нет. Обычно, когда я добавляю в обработчик событий, это длится для всего приложения, поэтому я не уверен, что происходит при постоянном добавлении и удалении обработчиков событий.

Ответы [ 6 ]

6 голосов
/ 27 сентября 2011

Технически, в этом коде нет ничего плохого.Поскольку после завершения метода не будет никаких ссылок на packet, подписка также будет (в конечном итоге) собрана.

В стиле я думаю, что делать это очень необычно.Лучшим решением, вероятно, было бы вернуть какой-то результат из Send() и основывать на этом ваше следующее действие.Последовательный код легче понять.

2 голосов
/ 27 сентября 2011

В этом и заключается прелесть программирования, основанного на событиях - вам неважно, кто / что / если кто-то слушает, вы просто говорите, что что-то произошло, и пусть кто-то, подписавшийся, реагирует соответствующим образом.

Вы неt имеет , чтобы отписаться вручную, но вы должны отменить подписку, когда объект выходит из области видимости.Ссылка будет поддерживать подписчиков в живых (от @pst).

1 голос
/ 27 сентября 2011

Я предполагаю, что вы упростили пример для краткости, но в вашем фрагменте нет необходимости использовать событие.Packet.Send() может просто вернуть проверенный результат.Вам действительно нужен более сложный подход, только если будет некоторая асинхронность (например, асинхронная операция, планирование для будущего выполнения и т. Д.), И Packet.Send() не возвращается немедленно.

В терминах объектаУправление жизненным циклом, ваша подписка на событие не представляет проблемы, так как подписка на событие будет поддерживать обработчик, но не наоборот.Таким образом, класс, к которому принадлежит button1_Click, не будет собирать мусор, пока существует действующая ссылка на пакет, на который он подписан.Поскольку пакеты имеют короткий срок службы, это не будет проблемой.

Если при использовании в реальных условиях Packet.Send() не может вернуть результат, то у меня будет соблазн передать делегата в пакетвместо того, чтобы подписываться на событие на нем, при условии, что только один объект должен быть уведомлен о сбое.

1 голос
/ 27 сентября 2011

Я понимаю, что вы можете получить утечку памяти, если источник события, то есть класс пакета, и приемник события, то есть обработчик события, имеют несоответствие времени жизни.В этом случае создаваемый вами делегат ограничивается этой функцией, поскольку приемник событий ограничивается только этой функцией.Вы можете сделать вызов packet.DataNotSent -= packet_DataNotSent; после выполнения метода send, чтобы убедиться, что делегат собран мусором.Пожалуйста прочитайте это

1 голос
/ 27 сентября 2011

Когда объект, публикующий событие, становится пригодным для сбора мусора, все объекты, подписанные на это событие, становятся также пригодными для сборки мусора (при условии, что на них нет других ссылок).

Итак, вВаш пример:

  • Когда packet выходит из области видимости, он становится доступным для GC.
  • Таким образом, объект, содержащий packet_DataNotSent, также становится подходящим для GC (если на него не ссылаетсячто-то еще).
  • Если объект, содержащий packet_DataNotSent , ссылается на что-то еще, он, конечно, не будет GC-ed, но он все равно будет автоматически "отменять подписку", когда packet является GC-ed.
0 голосов
/ 13 апреля 2012

События следует использовать для сценариев, в которых никто не знает, кому может быть интересно получать уведомления, когда что-то происходит или что-то становится необходимым.В вашем примере, если кто-то заинтересован в том, чтобы узнать, когда пакет будет обнаружен как недоставленный, код, вызывающий конструктор, может знать, кто это будет.Таким образом, было бы лучше иметь конструктор пакета, принимающий делегат Action<Packet, PacketResult>, чем публиковать событие для этой цели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...