Skip to content

[API Proposal]: A built-in collection for weak references #99308

Description

@huoyaoyuan

Background and motivation

Weak references are widely used for tracking objects meanwhile allowing them to be collected by GC. In current API, WeakReference<T> provides decent support for tracking individual objects. There is also need for weakly tracking a collection of objects effectively. Sample usages include:

  • Weak event pattern, register events and automatically deregister when the handler object is collected.
  • Data binding, an object listening changes to data source, or multiple-way binding of more objects, allowing each bound objects to be collected separately.

The current workarounds for managing collections of weak references including:

  • Use ConditionalWeakTable. The implementation of ArrayPool<T>.Shared uses the key of ConditionalWeakTable<TKey, TValue> to weakly track references. The value slots are not used and always set to null. This can lead to more overhead for the value field storing.
  • Manipulating WeakReference together with ConditionalWeakTable. Example: WeakEventManager of WPF.
  • Manually manipulate lists of WeakReference. When invoked, manually check each weak reference and remove collected ones.

To simplify weak reference management and reduce potential overhead, collections of weak references are desired. BCL built-in support also provides more confidence of correctness and efficiency.

API Proposal

namespace System
{
    public class WeakCollection<T> : IEnumerable<T>
        where T : notnull // or class instead?
    {
        public void Add(T item);
        public void Trim();
        public bool Contains(T item);
        public bool Remove(T item);

        public Enumerator GetEnumerator();

        public struct Enumerator : IEnumerator<T>
        {
            public bool MoveNext();
            public T Current { get; }
        }
    }
}

API Usage

class BindableObject
{
    private readonly WeakList<BindableObject> bounded;
    void InvokeValueChanged(string newValue)
    {
        foreach (BindableObject o in bounded)
        {
            if (bounded != this)
                bounded.NotifyValueChanged(newValue);
        }
    }
}

Alternative Designs

  • Implement ICollection<T>, IList<T>, or providing indexer ?
    Count and indexing are not clearly defined when some objects are collected by GC.

  • Make the collection aware of GC, or make GC aware of the collection?
    Ideally, the self-cleanup should run when GC happens. For maximum memory efficiency, GC may specially recognize this collection and mark the object references inside it as "weak". When GC moves the storage of the collection, it can also compact it. This would require on-heap weak reference support, which can be very challenging.
    Making the collection aware of GC should be considerably simpler. It can use WeakReference or GCHandle to track the objects, and skips collected objects during enumeration. Implicit trimming can be triggered for add, remove, or GC collecting events.

Risks

The complexity and correctness when providing more integrated support from GC.

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