DotNext by .NET Foundation and Contributors

<PackageReference Include="DotNext" Version="4.12.4" />

 SoftReference<T>

public sealed class SoftReference<T> : IOptionMonad<T>, ISupplier<object>, IFunctional<Func<object>>
Represents a form of weak reference which is eligible for garbage collection in Generation 2 only.
using DotNext.Runtime.CompilerServices; using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading; namespace DotNext.Runtime { [System.Runtime.CompilerServices.NullableContext(2)] [System.Runtime.CompilerServices.Nullable(0)] [DebuggerDisplay("State = {State}")] public sealed class SoftReference<[System.Runtime.CompilerServices.Nullable(1)] T> : IOptionMonad<T>, ISupplier<object>, IFunctional<Func<object>> where T : class { private sealed class Tracker : IGCCallback { private readonly SoftReferenceOptions options; private readonly SoftReference<T> parent; internal Tracker(SoftReference<T> parent, SoftReferenceOptions options) { this.options = options; this.parent = parent; } ~Tracker() { T strongRef = parent.strongRef; GCIntermediateReference trackerRef = parent.trackerRef; if (strongRef != null && trackerRef != null) { if (this != trackerRef.Target || !options.KeepTracking(strongRef)) try { trackerRef.Target = strongRef; } catch (InvalidOperationException) { } finally { parent.strongRef = null; } else GC.ReRegisterForFinalize(this); } } } [DebuggerBrowsable(DebuggerBrowsableState.Never)] private volatile T strongRef; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private volatile GCIntermediateReference trackerRef; [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 2 })] [DebuggerBrowsable(DebuggerBrowsableState.Never)] public (T Target, SoftReferenceState State) TargetAndState { [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 2 })] get { T val = strongRef; SoftReferenceState item; if (val != null) item = SoftReferenceState.Strong; else { val = (trackerRef?.Target as T); item = ((val != null) ? SoftReferenceState.Weak : SoftReferenceState.Empty); } return (val, item); } } [ExcludeFromCodeCoverage] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private SoftReferenceState State { get { return TargetAndState.State; } } private T Target => strongRef ?? (trackerRef?.Target as T); [DebuggerBrowsable(DebuggerBrowsableState.Never)] bool IOptionMonad<T>.HasValue { get { return Target != null; } } public SoftReference(T target, SoftReferenceOptions options = null) { if (target != null) { if (options == null) options = SoftReferenceOptions.Default; if (options.KeepTracking(target)) { strongRef = target; Tracker obj = new Tracker(this, options); trackerRef = new GCIntermediateReference(obj); GC.KeepAlive(obj); } else trackerRef = new GCIntermediateReference(target); } } private void ClearCore() { Interlocked.Exchange<GCIntermediateReference>(ref trackerRef, (GCIntermediateReference)null)?.Clear(); strongRef = null; } public void Clear() { ClearCore(); GC.SuppressFinalize(this); } public bool TryGetTarget([NotNullWhen(true)] out T target) { return (target = Target) != null; } [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] public Optional<T> TryGetTarget() { return new Optional<T>(Target); } [return: NotNullIfNotNull("defaultValue")] T IOptionMonad<T>.Or(T defaultValue) { return Target ?? defaultValue; } T IOptionMonad<T>.OrDefault() { get; } T IOptionMonad<T>.OrInvoke(Func<T> defaultFunc) { return Target ?? defaultFunc(); } bool IOptionMonad<T>.TryGet([NotNullWhen(true)] out T target) { return TryGetTarget(out target); } object ISupplier<object>.Invoke() { get; } public override string ToString() { return Target?.ToString(); } public static explicit operator T([System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] SoftReference<T> reference) { if (reference == null) return null; return reference.Target; } public static explicit operator Optional<T>([System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] SoftReference<T> reference) { return reference?.TryGetTarget() ?? Optional<T>.None; } ~SoftReference() { ClearCore(); } } }