В моем приложении.NET я подписываюсь на события от другого класса. Подписка является условным выражением. Я подписываюсь на события, когда управление видимо и de-подписка это, когда это становится невидимым. Однако в некоторых условиях я не хочу к de-subscribe событие, даже если управление не видимо, поскольку я хочу результат операции, которая происходит на фоновом потоке.
Существует ли путь, через который я могу определить, подписался ли класс уже на то событие?
Я знаю, что мы можем сделать это в классе, который сгенерирует то событие путем проверки события на null
, но как я делаю это в классе, который подпишется на то событие?
Ключевое слово 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;
}
/// <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);
}
Можете ли вы поместить логику принятия решения в метод, запускающий событие? Предположим, вы используете 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;
}
Разве вы не можете вспомнить, оформили ли вы уже подписку? До сих пор этот подход работал у меня хорошо. Даже если у вас много событий или объектов, вы все равно можете просто запомнить это (например, в словаре).
С другой стороны, изменение видимости было, по крайней мере, для меня, не лучшим поводом для подписки / отказа от подписки. Я обычно предпочитаю использовать конструкцию / Disposed, которые более понятны, чем при каждом изменении видимости.
Предполагая, что у вас нет доступа к внутренностям класса, объявляющего событие, у вас нет возможности сделать это напрямую. События открывают только операторы + =
и - =
, ничего больше. Вам понадобится флаг или какой-либо другой механизм в вашем классе подписки, чтобы знать, подписаны вы уже или нет.
Просто проверьте, отображается ли элемент управления при каждом запуске обработчика событий.