Publishing and consuming events
An example illustrating the problem
Imagine a class that has an object that publishes events and an object that consumes the events. Just by removing the references the object will never be collected, since the eventhandler still connects the two. We have to remove the eventhandler manually if we want the consumer to be collected by the garbage collector.
Show code snippet - Handling default events
//a class that shows how a normal eventhandler is added
public class MyExample
{
	//a class that consumes an event of MyPublisher
	private MyConsumer m_oConsumer = new MyConsumer();
	
	//a class that publishes an event
	private MyPublisher m_oPublisher = new MyPublisher();
	
	//a method that shows that the consumer is never finalized
	public void IllustrateProblem()
	{
		//add an eventhandler to the normal event
		m_oPublisher.MyNormalEvent += 
			new EventHandler(
				m_oConsumer.MyPublisher_MyNormalEvent
				);
		
		//raise the event
		m_oPublisher.RaiseNormalEvent();
		
		//remove the reference to the consumer
		m_oConsumer = null;
		
		//force a garbage collection to show that
		//the consumer is never finalized
		GC.Collect();
	}
}
	

An example illustrating the transparency of the solution
It can become a time-consuming process to use a memory profiler to check which objects in your application aren't being collected and which reference is the cause. This is why we have developed a QWeakDelegate. By merely changing the event of the MyPublisher class the eventhandler will become a WeakReference and the consumer will be freed normally. The following example uses a QWeakDelegate to publish events and it shows that no changes are neccessary for consuming events. Show code snippet - Handling QWeakEvents
//a class that shows how a weak eventhandler is added
public class MyExample
{
	//a class that consumes an event of MyPublisher
	private MyConsumer m_oConsumer = new MyConsumer();
	
	//a class that publishes an event
	private MyPublisher m_oPublisher = new MyPublisher();
	
	//a method that shows that the consumer is finalized
	//by using a weak event
	public void IllustrateSolution()
	{
		//add an eventhandler to the normal event
		m_oPublisher.MyWeakEvent += 
			new EventHandler(
				m_oConsumer.MyPublisher_MyWeakEvent
				);
		
		//raise the event
		m_oPublisher.RaiseWeakEvent();
		
		//remove the reference to the consumer
		m_oConsumer = null;
		
		//force a garbage collection to show that
		//the consumer is now finalized
		GC.Collect();
	}
}
	

How it works
We see now in the example above that for the consumer nothing actually changes. The change is completely transparent to the consumer. Of course, the publisher needs some code changes, but it's not a drastic change.
The code snippet below shows the way a normal event is published: Show code snippet - Publishing a default event
//a class that shows how normal events are published
public class MyPublisher
{
	//constructor
	public MyPublisher() {}

	//a normal event
	public event EventHandler MyNormalEvent;

	//an method that raises the event
	protected virtual void OnMyNormalEvent(EventArgs e)
	{
		if (MyNormalEvent != null)
		{
			MyNormalEvent(this, e);
		}
	}
}	
	

To change the event structure so that any consumers can be collected normally the following changes are neccesary: Show code snippet - Publishing a QWeakEvent
//a class that shows how weak events are published
public class MyPublisher : IQWeakEventPublisher
{
	private bool m_bWeakEventHandlers = true;
	
	private QWeakDelegate m_oMyWeakEventDelegate;

	//constructor
	public MyPublisher()
	{
		// Create a QWeakDelegate
		m_oMyWeakEventDelegate = new QWeakDelegate();
	}

	// Gets or sets if the eventhandlers should be weak referenced
	public bool WeakEventHandlers
	{
		get { return m_bWeakEventHandlers; }
		set { m_bWeakEventHandlers = value; }
	}

	//a weak event
	[QWeakEvent]
	public event EventHandler MyWeakEvent
	{
		add		
		{ m_oMyWeakEventDelegate = 
			QWeakDelegate.Combine(
				m_oMyWeakEventDelegate, 
				value, 
				this.WeakEventHandlers); }
		remove	
		{ m_oMyWeakEventDelegate = 
			QWeakDelegate.Remove(
				m_oMyWeakEventDelegate, 
				value); }
	}

	//a method that raises the event
	protected virtual void OnMyWeakEvent(EventArgs e)
	{
		m_oMyWeakEventDelegate = 
			QWeakDelegate.InvokeDelegate(
				m_oMyWeakEventDelegate, this, e);
	}
}	
	

Publishing events - Summary
To publish events with a weak reference, so that the eventhandler does not cause objects to remain uncollected by the garbage collector, use a QWeakEvent. The only differences when using a QWeakEvent are:
  • The publisher should be marked as IQWeakEventPublisher
  • The publisher should implement a boolean property WeakEventHandlers to indicate if newly added event handlers should be weak referenced or not.
  • Instead of declaring an event like: public event EventHandler MyEvent declare the event as a QWeakEvent: [QWeakEvent]
    public event EventHandler MyWeakEvent
    {
    add { m_oDelegate = QWeakDelegate.Combine(m_oDelegate, value, this.WeakEventHandlers); }
    remove { m_oDelegate = QWeakDelegate.Remove(m_oDelegate, value); }
    }
  • When raising the event instead of:
    if (MyEvent != null) MyEvent(this,e); use:
    m_oDelegate = QWeakDelegate.InvokeDelegate(m_oDelegate, this, e);

Consuming events
The publisher in the code snippet above publishes weak events, but you don't have control over all events. For instance, you can't change the way a button publishes the Click event. For these situations we have created the QWeakEventConsumer class. The code snippet below shows the MyConsumer class from the examples above. Show code snippet - Consuming default events
//a class that shows how classes normally consume events
public class MyConsumer
{
	//constructor
	public MyConsumer()	{}

	//a method that handles the event
	public void MyPublisher_MyNormalEvent(object sender, EventArgs e) {}
}

//...
//...
//...

//snipped from the Example class to attach the event
m_oPublisher.MyWeakEvent += 
	new EventHandler(m_oConsumer.MyPublisher_MyWeakEvent);
	
Both the consumer and publishers use the default way of consuming and publishing an event. To use a QWeakConsumer we change the code to the following: Show code snippet - Consuming default events with a QWeakConsumer
//a class that shows how classes consume methods by using a QWeakEventConsumer
public class MyWeakConsumer
{
	//a list of all QWeakEventConsumers
	public QWeakEventConsumerCollection EventConsumers;
	
	//constructor
	public MyConsumer()
	{
		//create the eventconsumers collection
		EventConsumers = new EventConsumers();
	}

	//a method that handles the event
	public void MyPublisher_MyNormalEvent(object sender, EventArgs e) {}
}

//...
//...
//...

//snipped from the Example class to attach the event
m_oConsumer.EventConsumers.Add(
	new QWeakEventConsumer(
		new EventHandler(
			tmp_oWeakConsumer.MyPublisher_MyNormalEvent), 
			m_oPublisher, 
			"MyNormalEvent"
			)
	);

	

Consuming events - Summary
Use QWeakEventConsumers for strong (normal) events from third party objects or objects from the .NET library.
The only differences when using a QWeakEventConsumers are:
  • The consumer gets a QWeakEventConsumerCollection that will contain all eventhandlers.
  • Instead attaching the event as: Publisher.Event += new EventHandler(Method) We attach the event by using the QWeakEventConsumers Collection: Consumer.EventConsumers.Add(new QWeakEventConsumer(new EventHandler(Method), Publisher, "Event"))
Qios.DevSuite.MemoryPack
Updated Version: 2006-09-07
Memory Management
Memory management
Introduction about automatic memory management and its problems.
More information
Publishing and consuming
Explains how to publish and consume events using the QWeakEvent structure to prevent controls from being ignored by the garbage collector.
More information
QWeakMessageFilter and
QWeakReferenceCollection
Shows how to create a collection of weak references and a weak IMessageFilter implementation.
More information
Other notes and links
A few other notes and links about memory management
More information
Qios.DevSuite.MemoryPack
Screenshots
Some MemoryPack screenshots that illustrate the difference when using our technique
More information
Download
Qios.DevSuite.MemoryPack
Download the free and opensource library & sample application that shows the difference between normal event handling and using the QWeakEvent structure.
Proceed to the download