Weak Event

  • 3 minutes to read

Developing an MVVM application may require you to declare events that don't need to be unregistered and don't cause memory leaks. Such events may be useful in organizing the interaction between different modules of the application (for example, data services and view models) and objects that exist throughout the entire application lifecycle.

The WeakEvent class allows you to declare weak events in the delegate void(object sender, object e) format. To declare a weak event, use the following syntax.

WeakEvent<EventHandler, EventArgs> myEvent = new WeakEvent<EventHandler, EventArgs>();
public event EventHandler MyEvent { add { myEvent.Add(value); } remove { myEvent.Remove(value); } }

void RaiseMyEvent(object sender, EventArgs e) {
    myEvent.Raise(sender, e);
}

Use Of Closures in Event Handlers

When you subscribe a weak event, that means that the handler is separated by the instance that owns the delegate and the method itself. The instance is stored as a weak reference. This allows you to ignore unsubscribing and to avoid memory leaks. However, this imposes certain limitations when you use a lambda expression as an event handler. Imagine that you have the following subscription:

//Incorrect approach
public class ViewModel {
    public ViewModel() {
        var service = ServiceLocator.Default.GetService<IService>();
        object localVariable = new object();
        //This event handler may be never invoked
        service.MyWeakEvent += (s, e) => {
            object closure = localVariable;
        };
    }
}

In this case, the lambda expression contains a closure to a local variable that is declared in the ViewModel's constructor. Since the compiler creates an intermediate object for the defined lambda with closures and this intermediate object is weakly referenced, nothing prevents the Garbage Collector from collecting the intermediate object, so your message handler may never be invoked.

To fix this potential issue with your weak event, declare your local variable as a property/field at the level of the object from which subscription is performed.

//Correct approach.
public class ViewModel {
    object LocalVariable {get; set;}
    public ViewModel() {
        var service = ServiceLocator.Default.GetService<IService>();
        //This event handler will be invoked correctly
        service.MyWeakEvent += (s, e) => {
            object closure = LocalVariable;
        };
    }
}

In this case, the event handler will be available throughout the entire lifecycle of the owner object.