Как определить, подписано ли событие
В моем приложении .NET я подписываюсь на события из другого класса. Подписка условна. Я подписываюсь на события, когда элемент управления видим и деблокирует его, когда он становится невидимым. Однако в некоторых случаях я не хочу отменять подписку на событие, даже если элемент управления не отображается, поскольку я хочу результат операции, которая происходит в фоновом потоке.
Есть ли способ, через который я могу определить, подписался ли класс на это событие?
Я знаю, что мы можем сделать это в классе, который поднимет это событие, проверив событие для null
, но как мне это сделать в классе, который будет подписаться на это событие?
Ответы
Ответ 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;
}
}