In the example below the Consumer object subscribes to the SampleData objects's SampleDataChanged event. This type of strong reference causes memory leaks in applications because the garbage collector will not clean up the event consumer even though the consumer may be out of scope. The result of my testing indicates that unfortunately there is still a strong reference because once the Consumer object goes out of scope and once Garbage collection has been called, calling the event on SampleData still causes the event to be raised in the Consumer meaning that the consumer has not been garbage collected.
Can I use the System.WeakReference object to resolve this issue? I will follow up on this shortly...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace StrongReferenceTestConsole
{
class Program
{
static void Main(string[] args)
{
SampleData sampleData = new SampleData();
for (int i = 0; i < 3; i++)
{
// Create a consumer which registers a method with the sample data objects SampleDataChanged event
using (Consumer consumer = new Consumer(sampleData))
{
consumer.Name = "Consumer " + i.ToString();
//Raise the sample data object's data changed event
sampleData.RaiseSampleDataChanged("Work on data - iteration : " + i.ToString());
}
}
sampleData.RaiseSampleDataChanged("Sample Data change - Consumer outside scope");
// Force garbage collection
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.ReadLine();
}
}
public class Consumer : IDisposable
{
IDisposable _subscription;
public Consumer(SampleData sampleData)
{
// The usual method for event subscription
//sampleData.SampleDataChanged += new SampleData.SampleDataChangedEventHandler(Consumer_SampleDataChanged);
//USE RX to subscribe to events
_subscription = Observable.FromEvent<SampleDataChangedEventArgs>(sampleData, "SampleDataChanged").Subscribe(
args =>
Consumer_SampleDataChanged(sampleData, args.EventArgs)
);
}
public string Name { get; set; }
void Consumer_SampleDataChanged(object sender, SampleDataChangedEventArgs e)
{
Console.WriteLine("Sample Data Changed : {0} - Notified Consumer : {1}", e.Message, this.Name);
}
~Consumer()
{
Console.WriteLine("Consumer finalizer");
}
#region IDisposable Members
public void Dispose()
{
//UNCOMMENT TO REMOVE MEMORY LEAK - IDisposable is used in Rx to release strong reference.
//if (_subscription != null) _subscription.Dispose();
Console.WriteLine("Consumer Dispose");
}
#endregion
}
/// <summary>
/// SampleData object which exposes event SampleDataChanged
/// </summary>
public class SampleData
{
public event EventHandler<SampleDataChangedEventArgs> SampleDataChanged;
public void RaiseSampleDataChanged(string message)
{
if (SampleDataChanged != null)
{
Console.WriteLine("SampleDataChanged event has " + SampleDataChanged.GetInvocationList().Count() + " delegates wired up.");
SampleDataChangedEventArgs e = new SampleDataChangedEventArgs(message);
SampleDataChanged(this, e);
}
}
}
public class SampleDataChangedEventArgs : EventArgs
{
public SampleDataChangedEventArgs(string message)
{
this.Message = message;
}
public string Message { get; set; }
}
}
Console output with memory leak:
Console output without memory leak: