I use WeakEventManager to avoid memory leaks, and I started to overuse them. I created extension methods, for example for INotifyPropertyChanged, for example:
public static void AddWeakPropertyChanged(this INotifyPropertyChanged item, Action handler)
{
PropertyChangedEventManager.AddHandler(item, (s, e) => handler(e.PropertyName), string.Empty);
}
Now I quickly realized that this did not work. In fact, you cannot use anonymous methods for poorly handling events. (If I understand correctly, the compiler creates a “closure class” (for storing reference values) for it, which has a handler, but since your closure class is not mentioned anywhere, GC will clear it and the event handler will not be called)
Question # 1: Is this correct? I mean, this is correct, then when using an anonymous method (or lambda) for a weak event handler, the handler is called only if the GC did not execute at the same time (for example, is it undefined)?
Well, I try so hard, so I did some unit tests to make sure everything was correct. It would seem that this is true until I click on the following unit test:
class DidRun
{
public bool Value { get; set; }
}
class TestEventPublisher
{
public event EventHandler<EventArgs> MyEvent;
public void RaiseMyEvent()
{
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}
}
class TestClosure
{
public DidRun didRun { get; set; }
public EventHandler<EventArgs> Handler { get; private set; }
public TestClosure()
{
this.Handler = new EventHandler<EventArgs>((s, e) => didRun.Value = true);
}
}
[TestMethod]
public void TestWeakReference()
{
var raiser = new TestEventPublisher();
var didrun = new DidRun();
var closure = new TestClosure { didRun = didrun };
WeakEventManager<TestEventPublisher, EventArgs>.AddHandler(raiser, "MyEvent", closure.Handler);
closure = null;
GC.Collect();
GC.Collect();
raiser.RaiseMyEvent();
Assert.AreEqual(false, didrun.Value);
}
Question # 2: Can someone explain to me why this test fails?
Expectation: here I have no closures (I pulled them out to make sure what is happening), I just have an object (closure) that subscribes to the event with WeakEventManager, and then I delete the link to it (closure = null;).
2 GC.Collect(), , WeakEventManager , . ?
EDIT: , ,