This project has moved. For the latest updates, please go here.
5
Vote

Messenger message dispatch does not work for lambdas with closures

description

Since Messenger uses weak reference to a target, message dispatch does not work if I use lambda with closure. This works:
void Register(SomeClass obj)
{
    Messenger.Default.Register<SomeMessage>(this, msg=>
    {
          MessageBox.Show(...);; // obj is not used, no closure
    });
}
This does not work:

void Register(SomeClass obj)
{
Messenger.Default.Register<SomeMessage>(this, msg=>
{
      MessageBox.Show(...);;
      obj.DoSomething();
});
}

When I send SomeMessage the handler is not called. The reason for that is that in the first case compiler generates just a method, but in the second case it generates a class that holds captured parameters ("obj" in our case). Register() is then called with a delegate pointing to an instance of that hidden class. Since messenger uses weak references, this instance quickly gets garbage collected, the reference dies, and the handler can no longer be called.

comments

leemcpherson wrote Jul 31, 2014 at 2:49 AM

Thank you for this... I believe it is the source of many problems I've been having.

lbugnion wrote Nov 23, 2016 at 10:26 AM

Hi,

I understand the frustration. Unfortunately as described in the Stackoverflow reference below, this problem just cannot be solved in an elegant manner.

http://stackoverflow.com/questions/25730530/bug-in-weakaction-in-case-of-closure-action

In the next version of MVVM Light, I will give the user the possibility to opt-in into a behavior where the reference to the anonymous class created by the closure will be kept no matter what. This will prevent the garbage collector to collect this instance (which is the reason why the WeakAction fails when you use closures).

To be clear, this will cause a (small) potential memory leak to appear. By opting into the behavior (concretely, by passing "true" to an optional parameter in the RelayCommand constructor and in the Register method of the Messenger), you will acknowledge that this is indeed the behavior that you prefer. If you don't want this, you can always use the default behavior, and avoid using a closure.

Hopefully this will solve the problem in a satisfactory, non-breaking manner.
I am planning to push this version as a preview this weekend. Please stay tuned.

Laurent

lbugnion wrote Nov 28, 2016 at 11:01 AM

Quick update after the weekend: I have a proposed implementation which would solve this issue but I am still looking for the best way to change the API (or not). I am contacting the C# team to run a few questions by them. If all goes well, I should be able to propose a poll on the change within a few days.

Laurent

lbugnion wrote Dec 4, 2016 at 5:41 PM

Hi,

I just published an alpha version of V5.4.0 to Nuget. This version proposes a fix to this issue. This requires setting a new parameter called keepTargetAlive to true when you call Register. Note that while this allows using a closure, this also risks causing a (small) memory leak.

I have preliminary documentation here: [http://www.mvvmlight.net/doc/weakaction.cshtml]. A more detailed article will be published as soon as I have time to finish it..

Hopefully this helps. This is an alpha, which means I am interested on your feedback.

Thanks!
Laurent