Roslyn: Proposal: C# language support of "weak" keyword

Created on 21 Sep 2016  路  21Comments  路  Source: dotnet/roslyn

Provide weak keyword for support weak events and weak references in C#

Weak events support (#101):

``` C#
someObject.Event += weak (sender, e) => {};
// translate to something like
System.Runtime.WeakEvent.Attach(someObject, nameof(TSomeObject.Event), (sender, e) => {});

Same `weak` keyword may be used to simplify work with regular weak references

``` C#
var obj = weak new List<string>();
// translate to 
var obj = new WeakReference<List<string>>(new List<string>());

public class AClass
{
    private weak List<string> weakList;
}

// translate to
public class AClass
{
    [WeakReference]
    private WeakReference<List<String>> weakList;
}

public weak List<string> SomeFunc()
{
     return new List<string>();
}
// translate to
[WeakReference]
public WeakReference<List<string>> SomeFunc()
{
    return new WeakReference<List<string>>();
}

As WeakRefecence<T> can not contain null - assignment of null should be used to indicate weak reference itself is null.

``` C#
List hardList;
weak ListString weakList = hardList;
// translate to
if (hardList == null)
{
weakList = null;
} else
{
weakList = new WeakReference>(hardList);
}

Usage of declared weak references in code

``` C#
// declare weak reference
weak List<string> weakList = new List<string>();
// same as
var weakList = weak new ListString();
// first declaration example is explicit variable type declaration, second - using "var" implicit typing

var cnt = weakList.Count;
// translate to:
int cnt;
{
    List<string> __weak_t;
    if (weakList != null && weakList.TryGetTarget(out __weak_t))
    {
        cnt = __weak_t.Count;
    } else
    {
        throw new InvalidWeakReferenceException();
    }
}

List<string> hardList = weakList;
// translate to
List<string> hardList;
if (weakList == null || !weakList.TryGetTarget(out hardList))
{
    throw new InvalidWeakReferenceException();
}

// use ?. operator to not throw exception if weak reference is invalid
var cnt = weakList?.Count;
// translate to
int? cnt;
{
    List<string> __weak_t;
    if (weakList != null && weakList.TryGetTarget(out __weak_t))
    {
        cnt = __weak_t.Count;
    } else
    {
        cnt = null;
    }
}

// adding ? at the end to not throw exception
List<string> hardList = weakList?;
// translate to
List<string> hardList;
if (weakList == null || !weakList.TryGetTarget(out hardList))
{
    hardList = null
}

// Checking if weak reference is alive by using ? operator
if (weakList? != null)
{
    Console.WriteLine("Weak reference is alive!");
}
// translate to
{
    List<string> __weak_t;
    if (weakList == null || !weakList.TryGetTarget(out __weak_t))
    {
        __weak_t = null;
    }
    if (__weak_t != null)
    {
        Console.WriteLine("Weak reference is alive!");
    }    
}

// Call a method
weakList.Clear();
// translate to
{
    List<string> __weak_t;
    if (weakList == null || !weakList.TryGetTarget(out __weak_t))
    {
        throw new InvalidWeakReferenceException();
    }
    __weak_t.Clear();
}

// Call a method not throwing an exception if reference is invalid
weakList?.Clear();
// translate to
{
    List<string> __weak_t;
    if (weakList != null && weakList.TryGetTarget(out __weak_t))
    {
        __weak_t.Clear();
    }
}

And types

``` C#
var t = typeof(weak List);
// same as
var t = typeof(WeakReference>);

weakList.GetType() // result: typeof(WeakReference>)
weakList?.GetType() // result: typeof(List)
```

Area-Language Design

Most helpful comment

Hello,

I would also like to see support for a "weak" keyword built into the language especially because the interoperability with iOS forces us to be extra careful of avoiding strong reference cycles.

Xamarin.iOS already has a [Weak] attribute that can be used for fields. However it would be useful if we could extend it for properties, events and delegates as well (actions and funcs).

There are often scenarios when using an action makes more sense than using an interface, and we're forced to use an interface just because it's easier to create a weak reference for it.

There are a few open source alternatives such as https://github.com/thomaslevesque/WeakEvent and https://github.com/lbugnion/mvvmlight/blob/master/GalaSoft.MvvmLight/GalaSoft.MvvmLight%20(PCL)/Helpers/WeakActionGeneric.cs but in my opinion they still require significant boiler plate / support code.

Giving that .NET 5 is heading towards closer interoperability with Swift / Objective-C this "weak" keyword would be very helpful.

Thank you,
Cosmin

All 21 comments

This seems like a heavy solution to a relatively rare problem. How often do you need to use weak references such that introducing a whole new language feature is worth the significant cost?

@MgSam Quite often. Weak references are often used in XAML apps. Especially often used is so called "weak event" pattern (it is supported out-of-the box in WPF, but isn't supported out-of-the-box in Silverlight and Windows Runtime/UWP XAML apps). So, weak references and weak events are common practice in complex UI frameworks and apps.

Proposal for weak keyword was done in general with #101 proposal for "weak event" pattern. This would generalize weak reference language support (not only support for weak events, but also for weak references in general at language level) and finish this topic.

Weak references are often used when lifetime of some objects isn't determined by garbage collector, but by external means - such as windows or pages user navigation or framework-controlled construction and destruction of UI controls. "Object graph visibility" lifecycle paradigm is often breaking here - as lifetime of many objects is determined by external means and if object with externally controlled lifetime is holding reference (often within event handler delegate) to other objects graph this would produce memory leaks or other undesired effects.

It's why weak references are often used in XAML UI apps.

It may be somewhat generalized to concept of "virtual references"

``` C#
public interface IVirtualReference where T : class
{
bool TryGetTarget(out T target);
}

Interface signature is exact as of `WeakReference<T>` class

"Virtual references" may support language-level dereferencing

``` C#
IVirtualReference<List<string>> vref;
List<string> list = vref;
// translate to
List<string> list;
if (vref == null || !vref.TryGetTarget(out hardList))
{
    throw new InvalidVirtualReferenceException();
}

...and ?. operator and "?" dereference without throwing an exception.

C# // Call a method not throwing an exception if reference is invalid vref?.Clear(); // translate to { List<string> __vref_t; if (vref != null && vref.TryGetTarget(out __vref_t)) { __vref_t.Clear(); } }

So this would make weak reference the special case of generalized virtual reference.

Virtual references would be useful for a wide range of cases, including proxies of any kind and interop scenarios.
It's another application for already introduced ?. operator.

@Opiumtm Weak references has some needs, but its needs is not so much.
On the other hand, C# wants to be more and more a High-Performance Language (see Themes in https://github.com/dotnet/roslyn/issues/98). Weak references goes against this theme. It has large negative impact on performance.

@ufcpp

but its needs is not so much

Are you sure? Weak references are very much used in XAML UI apps. I posted above why they're used in XAML UI apps.

Weak references are often used when lifetime of some objects isn't determined by garbage collector, but by external means - such as windows or pages user navigation or framework-controlled construction and destruction of UI controls. "Object graph visibility" lifecycle paradigm is often breaking here - as lifetime of many objects is determined by external means and if object with externally controlled lifetime is holding reference (often within event handler delegate) to other objects graph this would produce memory leaks or other undesired effects.
It's why weak references are often used in XAML UI apps.

WPF framework even have built-in support for weak reference patterns ("weak event" pattern in particular).

I agree, weak references as an "automatic tool" to solve UI-related object lifetime complexity (root cause for all these issues is a fact that any UI framework impose its own object lifetime scheme completely unrelated to GC "object visibility graph" paradigm as it is primarily driven by user interaction) isn't optimal at all, but it greatly simplify complex UI logic.

Most painful point in all .NET UI frameworks (and XAML UI in particular) is a fact that delegate is capturing hard reference to object and attachment of delegate to the UI control event effectively create object lifetime dependency on UI control via this delegate. To avoid this lifetime dependency event handlers must be manually removed (so you should maintain it in code) or to simplify things "weak event" pattern is used - especially if you're using or developing UI MVVM frameworks or helpers which haven't much knowledge about exact UI logic and structure of a particular app.

You are talking from the server-side perspective. Indeed, ASP.NET code haven't such painful dependencies on "external object lifetime" and should deliver best performance. But client-side UI code have these object lifetime dependencies (and major object lifetime dependency is the user UI actions themselves). Generalized solution applicable to MVVM frameworks and helpers (not knowing exact UI logic and structure) is to use weak references to avoid undesired implicit object lifetime dependencies.

Implicit lifetime dependencies on UI controls lifetime is a major source of memory leaks in XAML UI applications.

For example, Windows Runtime/UWP XAML app model support so called "navigation caching" and "UI virtualization caching" when UI controls and UI pages instances are re-used by framework if possible. Implicit object lifetime dependencies on these UI objects can cause massive memory leaks.

@Opiumtm
Typically, you should not need to explicitly use weak events in XAML apps in majority scenarios. It is necessary for some low-level or reusable library components. But for high-level application code, it is usually not the right solution.

The dangling event handlers are not only a memory leak issue. They may still run in case event is raised, and there is no guarantee when GC will actually remove the objects. So even if you have weak events, you have only solved half of the problem, if it is a real problem to solve in you app.

Typical case should be that your UI event handler and your ViewModel have the same lifetime as your UI, so you don't need to use weak events nor unsubscribe anything. If event emitter's life is longer than handler, it is always better to unsubscribe the event handler explicitly.

Weak event handlers are quite important in UI code and in situations where the inversion of control pattern is heavily applied. Novice developers often make mistakes, leaving event handlers attached and causing leaks. Language support for weak event handlers would be quite desirable!

@qrli

Typically, you should not need to explicitly use weak events in XAML apps

But in reality I am often forced to do so.
How do you implement subscription to some global event defined in static or long-living singleton object?

One example is a DisplayInformation on Universal Windows Platform. It's a global singleton object and if you subscribe to events such as DPI change (if your app is starting to be projected on PC display using Continuum feature), you will introduce memory leak. Also, Universal Windows apps typically have only one global Window object and if you subscribe to its events - you are forced to use weak events.

One another example is a subscription to global application events such as "Suspending" to do view-model specific work on application suspending (save its internal state to disk, obviously). Application is a global static object. If you attach events directly without weak event - you will immediately introduce memory leak.

@qrli

The dangling event handlers are not only a memory leak issue. They may still run in case event is raised, and there is no guarantee when GC will actually remove the objects. So even if you have weak events, you have only solved half of the problem, if it is a real problem to solve in you app.

For the most cases it isn't a problem.
When you finished with view model, you can dispose it explicitly or some other way indicate that view model is no longer valid, so view model wouldn't react to any events.

If event emitter's life is longer than handler, it is always better to unsubscribe the event handler explicitly.

You are wrong. On the view-model side you don't always know if your method is used as event handler (after all, Universal Windows XAML platform allow to bind void SomeMethod() as XAML event handlers) or bound to some delegating ICommand. View model fundamentally doesn't know how it is used from the outside and shouldn't know it. View model have no exact knowledge on how and where it is used in actual XAML markup UI.

And even if view model indeed subscribe to XAML events, it should maintain it and unsubscribe from all event handlers - which introduce lots of boilerplate and error-prone code. Weak event handlers is not optimal, but very simple and quite universal solution to avoid manual event handlers management.

Weak event handlers + view model state (alive or disposed) is the universal and simple solution to handle problems you described. Call Dispose() on view model once when you are finished with view model is much simpler than manually unsubscribe all the event handlers and maintain event handlers lists (and you may not have exact knowledge on such view model usage). View model would not react on any events in disposed state.

@qrli
And to conclude this topic, I must remind you one very useful practical rule.
You should use weak events when you don't know about target object lifecycle or when its lifecycle is independent from your object.

When two objects have independent lifecycles, controlled by independent means - weak events and weak references should always be used between them to avoid undesired implicit lifecycle dependencies.

What you really need is some sort of safer and easier way for disposable pattern, unsubscribing events is one of them. Weak events might not be good way for it, and looks like C# team is looking for better way.

@ufcpp as for now, weak references and weak events are commonly used in XAML UI apps. "Weak event" pattern was originally introduced in XAML WPF and as Silverlight and Windows Runtime/UWP doesn't support it out-of-the-box (as WPF does) it's perpetually reinvented in any XAML UI MVVM framework and library. Template10 UWP library, for example, not an exception - this lib again reinvented its own weak event helper classes and use weak events in library itself. As I have commited to Template10 library source code repository, I certainly know how and why it is used.

Template10 lib classes does subscribe on global singleton system events and without weak events it would introduce memory leaks.

@ufcpp
Here is example of weak event using in Template10 lib
https://github.com/Windows-XAML/Template10/blob/af72162201820343e62e5cfd8309d9e288a30880/Template10%20(Library)/Utils/DeviceUtils.cs

``` C#
private DeviceUtils(Common.WindowWrapper windowWrapper)
{
MonitorUtils = MonitorUtils.Current(windowWrapper);
WindowWrapper = windowWrapper ?? Common.WindowWrapper.Current();

        var di = windowWrapper.DisplayInformation();
        di.OrientationChanged += new Common.WeakReference<DeviceUtils, DisplayInformation, object>(this)
        {
            EventAction = (i, s, e) => i.Changed?.Invoke(i, EventArgs.Empty),
            DetachAction = (i, w) => di.OrientationChanged -= w.Handler
        }.Handler;

        var av = windowWrapper.ApplicationView();
        av.VisibleBoundsChanged += new Common.WeakReference<DeviceUtils, ApplicationView, object>(this)
        {
            EventAction = (i, s, e) => i.Changed?.Invoke(i, EventArgs.Empty),
            DetachAction = (i, w) => av.VisibleBoundsChanged -= w.Handler
        }.Handler;
    }

```

@Opiumtm
1st, all data bindings and command bindings are already weak referenced. You dont need another layer of weak reference.
2nd, once you use bindings, your view model won't have many event subscription from view. Even if you have, it typically does not matter because view-model typically has the same lifetime as view.
3rd, view-model are designed for views so it is called view-model. If you have a public event handler method, you know it would be used as event handler, strong-referenced.
4th, the only typical troublesome case, as you mentioned, is view-model subscribing events from singleton or other long-lived objects. In such scenarios, you should unsubscribe them on view close, and it is easier than to remember to check for view/view-model disposed state in every event handler, which is much more ease to introduce hard to reproduce bugs.

As for the case you don't know the actual lifecycle of objects, I proactively avoid that. Even in Javascript or python code, I do care about lifecycle, unless the process run once and then exit.

@qrli you may be right theoretically, but in practice weak references are commonly used in client XAML UI apps and was used in Windows Forms apps too.
When I personally program server-side ASP.NET or Windows Service code I never use weak references, this runtime feature is almost never needed in server-side code. But in client-side code it's used very often. Maybe it's because of absence of better tools for UI client apps. But it's a practice commonly used in .NET UI for years.

dotnet/corefx#12007

@ufcpp
I agree that the current weakreference has large negative impact on performance, but I think there should be solution to the problem. See https://github.com/dotnet/roslyn/issues/2171, weak ref should not have extra cost compared with strong ref.

@qrli weak events are very much a necessity when it comes to UWP XAML apps that have custom behaviors (and controls derived from built-in controls). Because the controls themselves are native C++, subscribing to an event such as KeyUp creates a ref cycle that the GC can't know about and the ref counted C++ will therefore always have a non-zero ref count. This is even more of a problem with custom C++ controls (non-framework) that are consumed by C#.
There is no good way to disconnect the event handlers in question, because Unloaded is not called synchronously, meaning if you were to unsubscribe in Unloaded, another Loaded event could have come in in the meantime and you'd be breaking your control (i.e. the element got re-added to the visual tree. Loaded, Loaded, Unloaded (corresponding to the first Loaded)).
The net result is a leaked control and possibly everything that touches that control depending on the circumstances.
This is a very common issue that there currently isn't a good workaround for other than creating weak event handler workarounds.

I would like to see the 'weak' modifier added, even if the C# language does not use it, but it would benefit others that use the compiler for other projects unrelated to a .NET framework (ie: do not use garbage collection).

I often use attributes to signal a weak reference::

[Weak]
MyObject some_variable;

But using a keyword would be much more natural.

Thanks.

Just a word to give you my support to add a Weak Event support to the language anyhow it could be implemented. More peoples will ask for it, more chances we have to get it one day! Nice work!

Hello,

I would also like to see support for a "weak" keyword built into the language especially because the interoperability with iOS forces us to be extra careful of avoiding strong reference cycles.

Xamarin.iOS already has a [Weak] attribute that can be used for fields. However it would be useful if we could extend it for properties, events and delegates as well (actions and funcs).

There are often scenarios when using an action makes more sense than using an interface, and we're forced to use an interface just because it's easier to create a weak reference for it.

There are a few open source alternatives such as https://github.com/thomaslevesque/WeakEvent and https://github.com/lbugnion/mvvmlight/blob/master/GalaSoft.MvvmLight/GalaSoft.MvvmLight%20(PCL)/Helpers/WeakActionGeneric.cs but in my opinion they still require significant boiler plate / support code.

Giving that .NET 5 is heading towards closer interoperability with Swift / Objective-C this "weak" keyword would be very helpful.

Thank you,
Cosmin

Was this page helpful?
0 / 5 - 0 ratings