(apologies if this has come up before, I've searched but not found anything for my search terms)
Given the following:
void Method1 {
Foo _foo = new Foo();
_foo.DataReady += ProcessData();
_foo.StartProcessingData();
}
void ProcessData() { //do something }
StartProcessingData()
is a long running method which eventually (and asynchronously) raises the DataReady
event. (let's say it does a service call or something)
Now, _foo
used to be a class-level variable, and the event used to be wired up in the constructor.
However, memory profiling highlighted how this would keep _foo
and all its dependents in memory forever, hence my change to the above.
My question is: is there ever a case when the GC will ruin things? Method1
finishes quickly (and certainly before the event fires), which means that _foo
ceases to be. However, does this mean that (because _foo keeps the references for its events) ProcessData()
will never fire? Or, is the presence of the event enough to keep _foo alive past the end of the method just enough to ensure that ProcessData fires? Or is it inconclusive?
[In testing, it's worked fine - ProcessData
is always called. Even making StartProcessingData
take a long time, and forcing a GC collection mid-way through (using RedGate's Memory Profiler) didn't remove it. But I'd like to be sure!]
For clarification: StartProcessingData()
returns immediately. The Foo
object would be something like:
class Foo
{
SomeSerice _service;
event EventHandler<EventArgs> DataReady;
Foo()
{
_service = new SomeService();
_service.ServiceCallCompleted += _service_ServiceCallCompleted;
}
void StartProcessingData()
{
_service.ServiceCallAsync();
}
void _service_ServiceCallCompleted
{
DataReady(null,e);
}
So, something that abstracts and emulates a long-running, asynchronous service using events to signal significant, uh, events.
Here's a complete, working example (a console app)
class Program
{
static void Main(string[] args)
{
Class1 _class1 = new Class1();
Console.WriteLine("Disposing of Class 1");
_class1 = null;
GC.Collect();
System.Threading.Thread.Sleep(15000);
Console.Read();
}
}
internal class Class1
{
internal Class1()
{
Foo _foo = new Foo();
_foo.DataReady += new EventHandler<EventArgs>(_foo_DataReady);
_foo.StartProcessingData();
}
void _foo_DataReady(object sender, EventArgs e)
{
Console.WriteLine("Class 1 Processing Data");
}
}
class Foo
{
internal event EventHandler<EventArgs> DataReady = delegate { };
internal void StartProcessingData()
{
System.Threading.Timer _timer = new System.Threading.Timer(OnTimer);
Console.WriteLine("Firing event in 10 secs");
_timer.Change(10000, System.Threading.Timeout.Infinite);
}
private void OnTimer(object state)
{
DataReady(this, null);
}
}
If you run it, you'll get:
Firing event in 10 secs
Disposing of Class 1
Class 1 Processing Data