-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: C# language support of "weak" keyword #13950
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 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" public interface IVirtualReference<T> where T : class
{
bool TryGetTarget(out T target);
} Interface signature is exact as of "Virtual references" may support language-level dereferencing 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. // 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. |
Are you sure? Weak references are very much used in XAML UI apps. I posted above why they're 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 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! |
But in reality I am often forced to do so. 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). |
For the most cases it isn't a problem.
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 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 |
@qrli 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 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 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. |
dotnet/corefx#12007 |
@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#. |
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::
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, |
I am in favor of the proposal for the weak keyword. Overall the use of weak events and weak collections introduces significant new flexibility into the language. Language support for WeakReferences makes the WeakReference class far easier to use, and if we believe that WeakReference is a very useful class then langauge support for it is merited. (1) In the MVVM paradigm, the suggested approaches that avoid weak references require the model elements to be rooted by the View and ViewModel. This is not necessarily the desired design approach as there are many cases where the Model element needs to be shared across multiple Views and must therefore have a lifetime exceeding the View and ViewModel lifetime. If the Model element has events then these need to be weak events because as we know there is not always a consistent place to call View.Dispose or ViewModel.Dispose for every View element (per @ali-hk the Unloaded event in particular is not such a place). (2) While it is true that the use of weak events can result in event callbacks occurring after the element (a ViewModel or View in the examples) is intended to be out of use, this is already a problem that needs to be solved for because in c# an event callback can occur after the event is unsubscribed from. Specifically the unsubscribe can occur after the event is raised but before all of the event callbacks complete ("zombie sender"). Therefore one needs to always check the sender in event callbacks anyway, as a general programming practice. So the suggestion that one should explicitly unsubscribe from events in Dispose does not address the concern that callbacks can occur after the item is intended to be out of use. In addition, in practice zombie callbacks from Model elements into ViewModel or View elements are seldom problematic because the callback action in the ViewModel or View is typically harmless. |
Closing this out. We're doing all language design now at dotnet/csharplang. If you're still interested in this idea let us know and we can migrate this over to a discussion in that repo. Thanks! |
Provide
weak
keyword for support weak events and weak references in C#Weak events support (#101):
Same
weak
keyword may be used to simplify work with regular weak referencesAs
WeakRefecence<T>
can not contain null - assignment of null should be used to indicate weak reference itself is null.Usage of declared weak references in code
And types
The text was updated successfully, but these errors were encountered: