Как стать автором
Обновить

Комментарии 16

Отличная статья!

Могу добавить к ней свой код очистки всех подписок на событие через Reflection:

        private static FieldInfo GetEventField(this Type type, string eventName)
        {
            FieldInfo field = null;
            while (type != null)
            {
                /* Find events defined as field */
                field = type.GetField(eventName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
                if (field != null && (field.FieldType == typeof(MulticastDelegate) || field.FieldType.IsSubclassOf(typeof(MulticastDelegate))))
                    break;
                
                /* Find events defined as property { add; remove; } */
                field = type.GetField("EVENT_" + eventName.ToUpper(), BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
                if (field != null)
                    break;
                type = type.BaseType;
            }
            return field;
        }

        public static void ClearEventInvocations(this object obj, string eventName)
        {
            var fi = obj.GetType().GetEventField(eventName);
            if (fi == null) return;
            fi.SetValue(obj, null);
        }




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

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

Спустя время, когда выдалась возможность, я наткнулся на недоделанное расширение и с помощью рефлектора и бутылки пива таки его сделал.

И, знаете, до сих пор ни разу не использовал! )))
В phoneGap нашёл следующий код:
        public void InvokeCustomScript(ScriptCallback script)
        {
            if (this.OnCustomScript != null)
            {
                this.OnCustomScript(this, script);
                this.OnCustomScript = null;
            }
        }

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

Отчего выбрано такое странное поведение?

Если вдруг список подписчиков на события опустел — это вызовет фатальную ошибку при отправке им события?

Зачем так сделано?

Гуры, поясните, если можете.

Чтоб не было фатальной ошибки и надо делать проверку на null перед вызовом. Ну или вариант в статье. Это проще, чем проверять ещё и количество, к примеру.
> Если вдруг список подписчиков на события опустел — это вызовет фатальную ошибку при отправке им события?
Подписчиками являются непосредственно методы, и при «отправке им события» мы вызываем все методы по порядку. Соответственно когда список методов пуст, есть два пути: не делать ничего или вызвать исключительную ситуацию. Второе обычно лучше, вызывает меньше непонимания в процессе отладки. Хуже, когда какие-то ошибки «проглатываются».
И вообще, делегат используется не только в механизме событий и отсутствие «ссылки на метод» как-то иначе кроме как пустой ссылкой показывать странно.
и еще — такой вопрос уже задавался команде разработчиков C#. Ответом было «мы не можем изменить имеющийся синтаксис вызова события через делегат, т.к. на это могли повязаться. Получается, что для решения этой проблемы нужно добавлять такую возможность к языку.» Также там было написано, что хотели сделать ключевое слово для вызова, но после обсуждения отказались от этой идеи.
Довольно распространенное заблуждение состоит в том, что порядок уведомления подписчиков не гарантируется.
Однако, это не так, ведь подписка на событие приводит в конечном счете к Delegate.Combine, который как раз гарантирует порядок вызова. То есть подписчики будут уведомляться в том порядке, в котором они подписывались.
Безусловно, рассчитывать на конкретный порядок — плохой стиль и надо пересмотреть дизайн приложения, но иногда бывают случаи, когда эта особенность полезна.
Мне кажется, что часто имеют в виду, что порядок подписки не может быть гарантирован. Во-первых, в большинстве случаев, когда подписчиков более 1-2, становится сложно проследить и гарантировать логический порядок подписки (особенно если они отписываются и снова подписываются в процессе работы). А во-вторых, в многопоточном окружении мы не можем знать, какой поток первым осуществит подписку.
Я бы не стал использовать эту особенность даже для простых случаев — такие порой становятся сложнее, а править «хаки» в них забывают. Не говоря уже о приятной отладке для других членов команды.
Хорошая статья, много нового узнал! Однако, чтобы статья была более полной, нужно упомянуть single-cast delegates.
Это мне известно. Однако в статье нет ни слова об этом.
Не очень вас понимаю. В статье написано, что все делегаты могут иметь произвольное количество ссылок на методы. Это не одно ли и то же, что они все — multicast? В любом случае теперь эти слова есть в комментариях.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории