D
DeveloperX
Hi, I've just written the following code to describe the issue I have.
In a nutshell if an object subscribes to an event on a long lived
object and I then ditch the reference to the subscriber (set it to
null) The object doesn't get GCed because the publisher still has a
reference to it.
Solutions I can think of are:
1) WeakReference - I'm not sure how to do that (Needs to be framework
1.1 and 2 compatible fyi) and I'd like to use events rather than
delegates directly if that makes sense.
2) Implement an interface to explicitly unsubscribe the subscribers
(Doesn't have to be an interface, just a method for simplicity) but
then I have to remember to call that. Not a huge hassle, and something
I already do in other applications.
Here's the code.
//Publisher first
-------------------------------------------------------
using System;
using System.Timers;
namespace Eventer
{
public delegate void TimerEventHandler(object sender, TimerEventArgs
e);
/// <summary>
/// Summary description for Publisher.
/// </summary>
public class Publisher
{
private Timer _timer=new Timer(2000);
public event TimerEventHandler TimerFired;
public Publisher()
{
_timer.Elapsed+=new ElapsedEventHandler(_timer_Elapsed);
_timer.Start();
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
TimerEventArgs nE=new TimerEventArgs(e.SignalTime.Ticks);
OnTimerFired(nE);
}
protected virtual void OnTimerFired(TimerEventArgs e)
{
if (null != TimerFired)
{
TimerFired(this,e);
}
}
}
public class TimerEventArgs : EventArgs
{
long _tick=0;
public TimerEventArgs(long pTick)
{
_tick = pTick;
}
public long Tick
{
get
{
return _tick;
}
}
}
}
// Subscriber
---------------------------------------------------------------------
using System;
namespace Eventer
{
/// <summary>
/// Summary description for Subscriber.
/// </summary>
public class Subscriber
{
int _value;
public Subscriber(int pValue, Publisher pPublisher)
{
_value = pValue;
pPublisher.TimerFired+=new TimerEventHandler(pPublisher_TimerFired);
}
private void pPublisher_TimerFired(object sender, TimerEventArgs e)
{
Console.WriteLine(_value.ToString() + " - " + e.Tick.ToString());
}
}
}
// The relevant bit of the form that tests it. Basically two buttons
one that starts and one that stops ------------------
private Publisher _publisber=new Publisher();
private System.Collections.ArrayList _arrayList=new ArrayList(5);
//Create subscribers
private void button1_Click(object sender, System.EventArgs e)
{
Subscriber s;
for(int c=0;c<5;c++)
{
s=new Subscriber(c,_publisber);
_arrayList.Add(s);
}
}
//clear reference to subscribers
private void button2_Click(object sender, System.EventArgs e)
{
_arrayList=null;
GC.Collect(); //trying to hurry things along for the demo
}
In a nutshell if an object subscribes to an event on a long lived
object and I then ditch the reference to the subscriber (set it to
null) The object doesn't get GCed because the publisher still has a
reference to it.
Solutions I can think of are:
1) WeakReference - I'm not sure how to do that (Needs to be framework
1.1 and 2 compatible fyi) and I'd like to use events rather than
delegates directly if that makes sense.
2) Implement an interface to explicitly unsubscribe the subscribers
(Doesn't have to be an interface, just a method for simplicity) but
then I have to remember to call that. Not a huge hassle, and something
I already do in other applications.
Here's the code.
//Publisher first
-------------------------------------------------------
using System;
using System.Timers;
namespace Eventer
{
public delegate void TimerEventHandler(object sender, TimerEventArgs
e);
/// <summary>
/// Summary description for Publisher.
/// </summary>
public class Publisher
{
private Timer _timer=new Timer(2000);
public event TimerEventHandler TimerFired;
public Publisher()
{
_timer.Elapsed+=new ElapsedEventHandler(_timer_Elapsed);
_timer.Start();
}
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
TimerEventArgs nE=new TimerEventArgs(e.SignalTime.Ticks);
OnTimerFired(nE);
}
protected virtual void OnTimerFired(TimerEventArgs e)
{
if (null != TimerFired)
{
TimerFired(this,e);
}
}
}
public class TimerEventArgs : EventArgs
{
long _tick=0;
public TimerEventArgs(long pTick)
{
_tick = pTick;
}
public long Tick
{
get
{
return _tick;
}
}
}
}
// Subscriber
---------------------------------------------------------------------
using System;
namespace Eventer
{
/// <summary>
/// Summary description for Subscriber.
/// </summary>
public class Subscriber
{
int _value;
public Subscriber(int pValue, Publisher pPublisher)
{
_value = pValue;
pPublisher.TimerFired+=new TimerEventHandler(pPublisher_TimerFired);
}
private void pPublisher_TimerFired(object sender, TimerEventArgs e)
{
Console.WriteLine(_value.ToString() + " - " + e.Tick.ToString());
}
}
}
// The relevant bit of the form that tests it. Basically two buttons
one that starts and one that stops ------------------
private Publisher _publisber=new Publisher();
private System.Collections.ArrayList _arrayList=new ArrayList(5);
//Create subscribers
private void button1_Click(object sender, System.EventArgs e)
{
Subscriber s;
for(int c=0;c<5;c++)
{
s=new Subscriber(c,_publisber);
_arrayList.Add(s);
}
}
//clear reference to subscribers
private void button2_Click(object sender, System.EventArgs e)
{
_arrayList=null;
GC.Collect(); //trying to hurry things along for the demo
}