Archives
-
05/01/2003 - 05/31/2003
06/01/2003 - 06/30/2003
07/01/2003 - 07/31/2003
08/01/2003 - 08/31/2003
09/01/2003 - 09/30/2003
01/01/2004 - 01/31/2004
02/01/2004 - 02/29/2004
06/01/2004 - 06/30/2004
07/01/2005 - 07/31/2005
11/01/2005 - 11/30/2005
Blog Roll
- lenn pryor's weblog
- John Porcaro's Weblog (MS Marketing Manager)
- Incessant Ramblings (Technology Entries)
- Harry Pierson's DevHawk Weblog
- Patrick Steele's .NET Blog
- The Mountain of Worthless Information (Ted Neward)
- Sam Ruby
- The Developer Life (senior VP of MS Dev Platform)
- Eric.Weblog() (Vault creator)
- Joel on Software
- Kent Sharkey's blog
- CraigBlog
- The Scobleizer Weblog
- Nikhil Kothari's Weblog
- Frans Bouma's blog
- Rob Howard's Blog
- Randy Holloway's Blog
- Dr. GUI's Bits and Bytes
- Ray Ozzie's Weblog
- Matt Pope's Radio Weblog
- Michael Helfrich's Radio Weblog
- Hugh's ramblings
- Groove DevZone Headlines
- John Burkhardt
- Paresh Suthar's Radio Weblog
- Samer Ibrahim's Blog
- ISerializable
- Musings from Gudge
- Julia Lerman Blog
- scotg.net
- Lutz Roeder's Weblog
- Adam Nathan's Interop-Centric CLR Blog
- Better Living Through Software
- Andres Aguiar's Weblog
- Jesse Ezell Blog
- News from the Forest - Justin Rudd
- tatochip
- simplegeek - Chris Anderson
- Drew's Blog
- Peter Drayton's Radio Weblog
- IUnknown.com: John Lam's Weblog on Software Development
- Y. B. Normal
- Sean 'Early' Campbell & Scott 'Adopter' Swigart's Radio Weblog
- Clemens Vasters: Enterprise Development & Alien Abductions
- Steve Swartz
- Shawn A. Van Ness's Blog
- Ingo Rammer's Weblog
- Brad Abrams
- Chris Brumme
- Code/Tea/Etc...
- Don Box's Spoutlet
- Pushing the Envelope - Ted Neward
- Sam Gentile's Blog
- MSDN Just Published
- MSDN: Visual Studio .NET
- MSDN: .NET Framework and CLR
- MSDN: XML Web Services
- MSDN: Visual C# .NET
- sellsbrothers.com: Windows Developer News
- The .NET Guy
- martinfowler.com: Updates
- Luke Hutteman's Weblog
- Fabrice's weblog
- Alex Lowe's .NET Blog
|
Feed your Head
Random Thought Patterns
a Xavier Musy weblog production
|
|
Sunday, June 06, 2004
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Weak Events
There's a not entirely uncommon WinForms scenario whereby a long-lived event publisher has short-lived event subscribers, in which the subscriber(s) mistakenly never detach from the events, thereby causing memory leaks (can't be GC'ed), or worse (exceptions or resurrection caused by the handlers in the disposed subscribers).
A partial solution to this problem is to use weak events, such that the GC can occur on the subscribers. It is only a partial solution, because the short-lived subscribers hang around until GC occurs; and, GC typically isn’t explicitly done. Even if it is, it would assume the publisher knows when a subscriber needs to detach or is disposed, which isn’t likely.
Greg Schechter blogs about a specific related scenario in Avalon, and posts an interesting solution to the problem. Ian Griffiths provides an alternative solution using generics and weak handlers (instead of weak delegates).
I’ve written an alternate solution below. It also addresses 2 other common problems: subscribers subscribing to the same handler more than once (solved using the CombineUnique method), and subscribers throwing unhandled exceptions, thereby stopping any remaining subscribers in the invocation list from receiving the events (solved using the InvokeSafe method).
public class WeakMulticastDelegate
{
private WeakReference weakRef;
private MethodInfo method;
private WeakMulticastDelegate prev;
public WeakMulticastDelegate(Delegate realDelegate)
{
this.weakRef = new WeakReference(realDelegate.Target);
this.method = realDelegate.Method;
}
public static WeakMulticastDelegate Combine(WeakMulticastDelegate weakDelegate, Delegate realDelegate)
{
if ( realDelegate == null )
return null;
return ( weakDelegate == null ) ? new WeakMulticastDelegate(realDelegate) : weakDelegate.Combine(realDelegate);
}
public static WeakMulticastDelegate CombineUnique(WeakMulticastDelegate weakDelegate, Delegate realDelegate)
{
if ( realDelegate == null )
return null;
return ( weakDelegate == null ) ? new WeakMulticastDelegate(realDelegate) : weakDelegate.CombineUnique(realDelegate);
}
private WeakMulticastDelegate Combine(Delegate realDelegate)
{
WeakMulticastDelegate head = new WeakMulticastDelegate( realDelegate );
head.prev = this.prev;
this.prev = head;
return this;
}
// provides unique subscribers: makes sure the the target & method are not already a subscriber
private WeakMulticastDelegate CombineUnique(Delegate realDelegate)
{
bool found = false;
if ( prev != null )
{
WeakMulticastDelegate curNode = prev;
while (!found && curNode != null )
{
if ( curNode.weakRef.IsAlive && curNode.weakRef.Target == realDelegate.Target &&
curNode.method == realDelegate.Method )
{
found = true;
}
curNode = curNode.prev;
}
}
return found ? this : Combine(realDelegate);
}
public static WeakMulticastDelegate Remove(WeakMulticastDelegate weakDelegate, Delegate realDelegate)
{
if ( realDelegate == null || weakDelegate == null )
return null;
return weakDelegate.Remove(realDelegate);
}
private WeakMulticastDelegate Remove(Delegate realDelegate)
{
if ( weakRef.IsAlive &&
weakRef.Target == realDelegate.Target &&
method == realDelegate.Method )
{
return this.prev;
}
WeakMulticastDelegate current = this.prev;
WeakMulticastDelegate last = this;
while ( current != null )
{
if ( current.weakRef.IsAlive &&
current.weakRef.Target == realDelegate.Target &&
current.method == realDelegate.Method )
{
last.prev = current.prev;
current.prev = null;
break;
}
last = current;
current = current.prev;
}
return this;
}
public void Invoke(object[] args)
{
WeakMulticastDelegate current = this;
while ( current != null )
{
if ( current.weakRef.IsAlive )
{
current.method.Invoke(current.weakRef.Target, args);
}
current = current.prev;
}
}
// provides safe invocation: such that a subscriber throwing in it's handler won't stop
// other subscribers from receiving
public void InvokeSafe(object[] args)
{
WeakMulticastDelegate current = this;
while ( current != null )
{
if ( current.weakRef.IsAlive )
{
try
{
current.method.Invoke(current.weakRef.Target, args);
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
current = current.prev;
}
}
}
A publisher can expose an event in the following manner:
private WeakMulticastDelegate weakEventHandler = null;
public event EventHandler SomeEvent
{
add
{
weakEventHandler = WeakMulticastDelegate.CombineUnique(weakEventHandler, value);
}
remove
{
weakEventHandler = WeakMulticastDelegate.Remove(weakEventHandler, value);
}
}
And can fire the event this way:
if ( this.weakEventHandler != null )
weakEventHandler.Invoke( new object[]{this, new EventArgs()} );
The information in this weblog is provided "AS IS"
with no warranties, and confers no rights. This weblog does not represent
the thoughts, intentions, plans or strategies of Seed Industries. It is solely
my opinion.
Copyright © 2003, Xavier Musy. All right are reserved.
Copyright © 2003, Xavier Musy. All right are reserved.

