| File: System\Windows\Forms\RefCountedCache.Scope.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms.Primitives\src\System.Windows.Forms.Primitives.csproj (System.Windows.Forms.Primitives) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Windows.Forms; internal abstract partial class RefCountedCache<TObject, TCacheEntryData, TKey> { /// <summary> /// Disposable struct that manages reference counting of <see cref="CacheEntry"/>. /// </summary> #if DEBUG internal class Scope : DisposalTracking.Tracker, IDisposable #else internal readonly ref struct Scope #endif { private readonly TObject _object; private CacheEntry Entry { get; } /// <summary> /// Constructor to hold an uncached object. Used to wrap something not coming from the cache in a scope /// so it can be abstracted for the end users of a given API. /// /// See <see cref="GdiPlusCache.GetSolidBrushScope(Drawing.Color)"/> for an example. /// </summary> /// <remarks> /// <para> /// Currently we don't dispose the <paramref name="object"/> as we don't need to in our usages. If this /// becomes necessary we can add a bool to track whether or not we should dispose it. /// </para> /// </remarks> public Scope(TObject @object) { Entry = default!; _object = @object; } public Scope(CacheEntry entry) { Debug.Assert(entry is not null); _object = default!; Entry = entry; Entry.AddRef(); } [MaybeNull] public TCacheEntryData Data => Entry is null ? default : Entry.Data; public TObject Object => this; public int RefCount => Entry?.RefCount ?? -1; /// <summary> /// Implicit conversion to the "target" type, i.e. <typeparamref name="TObject"/>. /// </summary> /// <remarks> /// <para> /// This is somewhat dangerous as implicit casting in the using statement will leak the scope. Not doing /// this, however, makes usage with APIs difficult. We track in DEBUG to catch misuse as a mitigation. /// </para> /// </remarks> public static implicit operator TObject(in Scope scope) { #if DEBUG // In DEBUG the scope is a class and we create "default" scopes in some cases. if (scope is null) { return default!; } #endif CacheEntry entry = scope.Entry; return entry is null ? scope._object : entry.Object; } public void Dispose() { Entry?.RemoveRef(); DisposalTracking.SuppressFinalize(this!); } #if DEBUG // Only need to define this constructor when we are a class internal Scope() { Entry = default!; _object = default!; } #endif } }