Установка / удаление обработчиков событий в .Net - PullRequest
4 голосов
/ 04 марта 2009

Так что я застрял с исправлением / обслуживанием другого кода программистов (блех)

Я твердый профессор правила «Если оно не сломано, не чините его!» поэтому глупо хотеть что-то менять каждый раз, когда сталкиваюсь с ужасным кодом, я ограничиваю себя только Изменение абсолютного минимального объема кода возможно для внесения необходимых исправлений. Но в некоторых случаях мне действительно нужно что-то понять, прежде чем пытаться следовать этому / изменить это.

Я столкнулся с этим немного здесь:

region.LineSelected = (x) => { };

И мне интересно, если это так же, как это:

region.LineSelected = null;

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

Ответы [ 7 ]

8 голосов
/ 04 марта 2009

Редактировать на основе моего текущего мнения по теме

Они не одинаковы. Лямбда-версия добавляет обработчик событий в пустой анонимный метод. Это позволяет другому коду свободно вызывать LineSelected (), не беспокоясь о том, является ли он пустым (т. Е. Не имеет прослушивателей).

Например.

var lineSelected = this.LineSelected;

if (lineSelected != null)
{
    lineSelected(EventArgs.Empty);
}

Приведенный выше оператор может генерировать исключение NullReferenceException, если что-то отписывается от LineSelected в другом потоке после if, но до возникновения события. Присвоение LineSelected временной переменной, а затем ее вызов, который может вызвать неподписанного прослушивателя событий. Назначение обработчика событий локальной переменной - рекомендуемый метод обработки нулевых делегатов.

Добавляя пустой делегат, другой код всегда может вызывать LineSelected, не опасаясь NullReferenceException. Назначая делегаты многоадресного события локальной переменной, вы можете быть уверены, что значение не может быть изменено другой веткой.

2 голосов
/ 04 марта 2009

Это не обработчик событий, это простой делегат. (Обработчик события должен быть изменен с помощью + = и - =, чтобы присоединить и отсоединить событие).

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

Это может сделать вызов кода более удобным, но его не следует путать с улучшением производительности (устраняя необходимость проверки на нулевое значение). Как правило, накладные расходы при вызове метода делегата будут значительно выше, чем при нулевой проверке. Могут быть некоторые сценарии (например, если у делегата «реальная» реализация в 99,99% случаев), когда отказ от нулевой проверки может повысить производительность, но трудно представить сценарий, в котором небольшая разница в производительности может иметь значение, достаточное для стоило того, что также не потребовало бы удаления вызова делегата полностью в пользу чего-то более эффективного.

2 голосов
/ 04 марта 2009

Я не могу думать ни о какой причине, чтобы иметь эту первую строку кода. Единственное, о чем я могу подумать, это при вызове события LineSelected, если у вас есть первая строка кода в классе, вам не нужно проверять, является ли событие LineSelected нулевым. то есть:

if (this.LineSelected != null)
{
   LineSelected(this,new EventArgs());
}

Вместо этого вы можете просто вызвать событие без нулевых проверок.

Однако со второй строкой кода вам нужно будет проверить наличие нулей.

1 голос
/ 04 марта 2009

Чтобы расширить то, что сказали Ричард и Эндрю, это эквивалент

region.LineSelected = delegate {};

Это означает, что когда вы поднимаете событие, вам не нужно сначала проверять наличие нуля, потому что у него есть делегат (по цене небольшого удара по производительности)

1 голос
/ 04 марта 2009

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

Если код вызова событий LineSelected не имеет надлежащей нулевой проверки, это вызовет исключение:

region.LineSelected = null;

/* no event handlers added to LineSelected */

class Region {
    void OnLineSelected() {
        // Null error!
        LineSelected();
    }
}

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

1 голос
/ 04 марта 2009

Они не одинаковы, потому что установлен обработчик события.

Допустим, класс, который предоставляет доступ к LineSelected, забыл:

if( LineSelected != null )
    LineSelected(...)

Если этот класс будет вызывать LineSelected и никто его не слушает, то он выдаст исключение NullReferenceException

Обратите внимание, что вы также можете сделать (внутри региона), чтобы избежать условия гонки:

var event = LineSelected; если (событие! = ноль) событие (...

1 голос
/ 04 марта 2009

Нет, это не одно и то же - первая строка назначает LineSelected пустой делегат, который сильно отличается от null.

Самый простой способ обнаружить разницу - это посмотреть код, который компилятор генерирует от вашего имени, когда вы используете лямбда-синтаксис. Этот код:

using System;

class Program
{
    static void Main()
    {
        Action<int> func0 = (x) => { };
        Action<int> func1 = null;
    }
}

Действительно компилируется в это:

internal class Program
{
    // Methods
    private static void Main()
    {
        Action<int> func0 = delegate (int x) {
        };
    }
}

Обратите внимание, что компилятор был достаточно умен, чтобы удалить func1, так как он был установлен на null и не упоминался в других местах. Но обратите внимание, что func0 все еще остается и настроен на делегата, который хоть и ничего не делает, но сильно отличается от null.

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