In a few editions of my book, I showed how to implement weak event handlers which allow an object to be GCd if no other reference is keeping the object alive. If the object is alive, it recieves the event notification and if it isn’t alive, it doesn’t receive the event notification. It has come to my attention that the code I show in my book is incorrect and does not work as advertised. I have corrected that code and the working version is shown below:
public static class WeakEventHandler {
private class WeakEventHandlerImpl {
protected readonly WeakReference m_wrTarget; // WeakReference to original delegate’s target object
protected Delegate m_openEventHandler; // “Open” delegate to invoke original target’s delegate method
public WeakEventHandlerImpl(Delegate d) { m_wrTarget = new WeakReference(d.Target); }
// Match is used to compare a WeakEventHandlerImpl object with an actual delegate.
// Typically used to remove a WeakEventHandlerImpl from an event collection.
public Boolean Match(Delegate strongEventHandler) {
// Returns true if original target & method match the WeakEventHandlerImpl’s Target & method
return (m_wrTarget.Target == strongEventHandler.Target) && (m_openEventHandler.Method == strongEventHandler.Method);
}
}
// “Open” delegate definition to quickly invoke original delegate’s callback
private delegate void OpenEventHandler<TTarget, TEventArgs>(TTarget target, Object sender, TEventArgs eventArgs)
where TTarget : class
where TEventArgs : EventArgs;
// A proxy object that knows how to invoke a callback on an object if it hasn’t been GC’d
private sealed class WeakEventHandlerImpl<TEventHandler> : WeakEventHandlerImpl where TEventHandler : class {
// Refers to a method that removes a delegate to this proxy object once we know the original target has been GC’d
private readonly Action<TEventHandler> m_cleanup;
// This is the delegate passed to m_cleanup that needs to be removed from an event
private readonly TEventHandler m_proxyHandler;
public static TEventHandler Create(TEventHandler eh, Action<TEventHandler> cleanup) {
Contract.Requires(eh != null && cleanup != null);
// We don’t create weak events for static methods since types don’t get GC’d
Delegate d = (Delegate)(Object)eh; // We know that all event handlers are derived from Delegate
if (d.Target == null) return eh;
var weh = new WeakEventHandlerImpl<TEventHandler>(d, cleanup);
return weh.m_proxyHandler; // Return the delegate to add to the event
}

60; private WeakEventHandlerImpl(Delegate d, Action<TEventHandler> cleanup) : base(d) {
m_cleanup = cleanup;
Type targetType = d.Target.GetType();
Type eventHandlerType = typeof(TEventHandler);
Type eventArgsType = eventHandlerType.IsGenericType
? eventHandlerType.GetGenericArguments()[0]
: eventHandlerType.GetMethod(“Invoke”).GetParameters()[1].ParameterType;
// Create a delegate to the ProxyInvoke method; this delegate is registered with the event
var miProxy = typeof(WeakEventHandlerImpl<TEventHandler>)
.GetMethod(“ProxyInvoke”, BindingFlags.Instance | BindingFlags.NonPublic)
.MakeGenericMethod(targetType, eventArgsType);
m_proxyHandler = (TEventHandler)(Object)Delegate.CreateDelegate(eventHandlerType, this, miProxy);
// Create an “open” delegate to the original delegate’s method; ProxyInvoke calls this
Type openEventHandlerType = typeof(OpenEventHandler<,>).MakeGenericType(d.Target.GetType(), eventArgsType);
m_openEventHandler = Delegate.CreateDelegate(openEventHandlerType, null, d.Method);
}
private void ProxyInvoke<TTarget, TEventArgs>(Object sender, TEventArgs e)
where TTarget : class
where TEventArgs : EventArgs {
// If the original target object still exists, call it; else call m_cleanup to unregister our delegate with the event
TTarget target = (TTarget)m_wrTarget.Target;
if (target != null)
((OpenEventHandler<TTarget, TEventArgs>)m_openEventHandler)(target, sender, e);
else m_cleanup(m_proxyHandler);
}
}
// We offer this overload because it is so common
public static EventHandler Wrap(EventHandler eh, Action<EventHandler> cleanup) {
return WeakEventHandlerImpl<EventHandler>.Create(eh, cleanup);
}
public static TEventHandler Wrap<TEventHandler>(TEventHandler eh, Action<TEventHandler> cleanup) where TEventHandler : class {
return WeakEventHandlerImpl<TEventHandler>.Create(eh, cleanup);
}
public static EventHandler<TEventArgs> Wrap<TEventArgs>(EventHandler<TEventArgs> eh, Action<EventHandler<TEventArgs>> cleanup) where TEventArgs : EventArgs {
return WeakEventHandlerImpl<EventHandler<TEventArgs>>.Create(eh, cleanup);
}
public static Boolean Match(Delegate weakEventHandler, Delegate strongEventHandler) {
return ((WeakEventHandlerImpl) weakEventHandler.
Target).Match(strongEventHandler);
}
}
To use it, instead of registerring an event callback like this:
someButton.Click += o.ClickHandler;
Do this:
someButton.Click += WeakEventHandler.Wrap(o.ClickHandler, eh => someButton.Click -= eh);
Always Here For You – 24x7x365
Atmosera has hired and developed an industry-leading team of skilled engineers, architects, technicians, systems specialists and project managers.
We offer a wealth of real world experience and proven best practices.
We are also committed to staying abreast of the latest technology vendors and offerings.
System Status and Outages
Date
All Green
Service Impacted
All Systems and Services are Fully Operational
Comments
Stay Connnected
Maintenance notices, newsletter, news flash, events, and email preferences.
Access Web Help Desk
Web Help Desk, or WHD for short, is a web-based application built on the SolarWinds Web Help Desk software. If you are an authorized WHD user, use your credentials to login.