Skip to content

[API Proposal]: Add weak event listener helper classΒ #61517

@dotMorten

Description

@dotMorten

Background and motivation

It's quite common in MVVM scenarios, that you will bind your models and viewmodels to UI controls. These UI controls will often listen for PropertyChanged and CollectionChanged events to update the UI dynamically. However since the UI views are quite often transient but the model data is long lived, you often see very expensive UI controls getting stuck in memory due to the event handler not getting unsubscribed. This problem is of course not limited to UI components only, but where you'll often see large costs associated with not unsubscribing from the events.

A typical pattern for this is to use a helper class to subscribe to weak event handlers, and over and over again we see different implementations of this in various libraries. So much so recently .NET MAUI decided to graduate their toolkit helper to the main MAUI library. This got me thinking that since this is such a common scenario, that .NET should provide this at a lower level for all libraries to use.

Earlier I had suggested that this should be a language feature, but it was concluded this should be an API feature: dotnet/roslyn#101

Example of various implementations:

API Proposal

I think there are quite a few different approaches that can be taken, as shown with all the implementations above. I'd refer to the .NET MAUI example to start with:

public void AddEventHandler(Delegate? handler, [CallerMemberName] string eventName = null)
{
	ArgumentNullException.ThrowIfNull(eventName);
	ArgumentNullException.ThrowIfNull(handler)

	var methodInfo = handler.GetMethodInfo() ?? throw new NullReferenceException("Could not locate MethodInfo");

	AddEventHandler(eventName, handler.Target, methodInfo, eventHandlers);
}

public void RemoveEventHandler(Delegate? handler, [CallerMemberName] string eventName = "")
{
	ArgumentNullException.ThrowIfNull(eventName);
	ArgumentNullException.ThrowIfNull(handler)

	var methodInfo = handler.GetMethodInfo() ?? throw new NullReferenceException("Could not locate MethodInfo");

	RemoveEventHandler(eventName, handler.Target, methodInfo, eventHandlers);
}

API Usage

readonly WeakEventManager weakEventManager = new WeakEventManager();

event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged
{
    add => weakEventManager.AddEventHandler(value);
    remove => weakEventManager.RemoveEventHandler(value);
}

void OnPropertyChanged([CallerMemberName] in string propertyName = "") => weakEventManager.HandleEvent(this, new PropertyChangedEventArgs(propertyName), nameof(INotifyPropertyChanged.PropertyChanged));

Alternative Designs

Windows Community Toolkit:
WeakEventListener.cs

var weakEvent = new WeakEventListener<INotifyCollectionChanged, object, NotifyCollectionChangedEventArgs>(valNotifyCollection)
{
    OnEventAction = (instance, source, args) => obj.SetActive(IsNullOrEmpty(instance)),
    OnDetachAction = (weakEventListener) => valNotifyCollection.CollectionChanged -= weakEventListener.OnEvent
};

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Runtimeneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions