In my .NET application I am subscribing to events from another class. The subscription is conditional. I am subscribing to events when the control is visible and de-subscrib
Simply check whether the control is visible or not whenever the event handler is triggered.
Can't you just remember whether you already subscribed? That approach worked fine for me so far. Even if you have a lot of events or objects, you may still want to just remember that (in a dictionary, for example).
On the other hand, visibility change was, at least for me, not a good point to subscribe/unsubscribe. I typically rather go with construction / Disposed, which are more clear than each time visibility changes.
I'm just expanding on Hans' answer. I'm just trying to ensure that I'm not installing my handler more than once, and not removing it when I still need it. This doesn't protect from a malicious or malfeasant caller unsubscribing repeatedly, for that you'd need to track the callers, and that would just open you up to having repeated subscriptions overrun the tracking mechanism.
// 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;
}
}
The event
keyword was explicitly invented to prevent you from doing what you want to do. It restricts access to the underlying delegate
object so nobody can directly mess with the events handler subscriptions that it stores. Events are accessors for a delegate, just like a property is an accessor for a field. A property only permits get and set, an event only permits add and remove.
This keeps your code safe, other code can only remove an event handler if it knows the event handler method and the target object. The C# language puts an extra layer of security in place by not allowing you to name the target object.
And WinForms puts an extra layer of security in place so it becomes difficult even if you use Reflection. It stores delegate
instances in an EventHandlerList
with a secret "cookie" as the key, you'd have to know the cookie to dig the object out of the list.
Well, don't go there. It is trivial to solve your problem with a bit of code on your end:
private bool mSubscribed;
private void Subscribe(bool enabled)
{
if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged;
else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged;
mSubscribed = enabled;
}
Assuming that you have no access to the innards of the class declaring the event, you have no way to do it directly. Events only expose operators +=
and -=
, nothing else. You will need a flag or some other mechanism in your subscribing class to know whether you are already subscribed or not.
/// <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);
}