Подтвердить что ты не робот

Как определить, подписано ли событие

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

Есть ли способ, через который я могу определить, подписался ли класс на это событие?

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

4b9b3361

Ответ 1

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

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

И WinForms добавляет дополнительный уровень безопасности, поэтому становится трудно, даже если вы используете Reflection. Он хранит экземпляры delegate в EventHandlerList с секретным "cookie" в качестве ключа, вам нужно знать куки, чтобы выкопать объект из списка.

Ну, не ходи туда. Тривиально решить вашу проблему с помощью небольшого кода на вашем конце:

private bool mSubscribed;

private void Subscribe(bool enabled)
{
    if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged;
    else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged;

    mSubscribed = enabled;
}

Ответ 2

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

Ответ 3

  /// <summary>
  /// Determine if a control has the event visible subscribed to
  /// </summary>
  /// <param name="controlObject">The control to look for the VisibleChanged event</param>
  /// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns>
  private bool IsSubscribed(Control controlObject)
  {
     FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible",
        BindingFlags.Static | BindingFlags.NonPublic);
     object object_value = event_visible_field_info.GetValue(controlObject);
     PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events",
        BindingFlags.NonPublic | BindingFlags.Instance);
     EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null);
     return (event_list[object_value] != null);
  }

Ответ 4

Можете ли вы поместить логику принятия решений в метод, который запускает событие? Предполагая, что вы используете Winforms, он выглядит примерно так:

 if (MyEvent != null && isCriteriaFulfilled)
{
    MyEvent();
}

Где isCriteriaFulfilled определяется вашей видимой/невидимой логикой.

//ОБНОВЛЕНИЯ/////

В дополнение к вашему 1-му комментарию было бы нецелесообразно изменять поведение внутри обработчика событий в зависимости от значения this.Visible?

 a.Delegate += new Delegate(method1);
...
private void method1()
{
    if (this.Visible)
        // Do Stuff
}

Или если вам действительно нужно подписаться и отказаться от подписки:

 private Delegate _method1 = null;
...
if(this.visible) 
{
    if (_method1 == null)
        _method1 = new Delegate(method1);
    a.Delegate += _method1;
}
else if (_method1 != null)
{
    a.Delegate -= _method1;
} 

Ответ 5

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

Ответ 6

Разве ты не помнишь, подписали ли вы? Этот подход работал у меня до сих пор. Даже если у вас много событий или объектов, вы все равно можете просто запомнить это (например, в словаре).

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

Ответ 7

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

// Tracks how many times the ReflectionOnlyResolveHandler has been requested.
private static int  _subscribers = 0;

/// <summary>
/// Register or unregister the ReflectionOnlyResolveHandler.
/// </summary>
/// <param name="enable"></param>
public static void SubscribeReflectionOnlyResolve(bool enable)
{
    lock(_lock)
    {
        if (_subscribers > 0 && !enable) _subscribers -= 1;
        else if (enable) _subscribers += 1;

        if (enable && _subscribers == 1) 
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler;
        else if (_subscribers == 0) 
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler;
    }
}