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

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

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

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

51
задан Mark Good 7 April 2018 в 15:30
поделиться

6 ответов

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

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

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

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

private bool mSubscribed;

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

    mSubscribed = enabled;
}
64
ответ дан 7 November 2019 в 10:15
поделиться
  /// <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
ответ дан 7 November 2019 в 10:15
поделиться

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

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

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

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

В дополнение к вашему первому комментарию, не имеет ли смысла изменять поведение внутри вашего обработчика событий в зависимости от значения 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;
} 
1
ответ дан 7 November 2019 в 10:15
поделиться

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

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

0
ответ дан 7 November 2019 в 10:15
поделиться

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

7
ответ дан 7 November 2019 в 10:15
поделиться

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

1
ответ дан 7 November 2019 в 10:15
поделиться