File: src\runtime\src\libraries\System.Private.CoreLib\src\System\WeakReference.T.cs
Web Access
Project: src\runtime\src\coreclr\nativeaot\System.Private.CoreLib\src\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using static System.WeakReferenceHandleTags;

namespace System
{
    [Serializable]
    [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
    // This class is sealed to mitigate security issues caused by Object::MemberwiseClone.
    public sealed partial class WeakReference<T> : ISerializable
        where T : class?
    {
        // If you fix bugs here, please fix them in WeakReference at the same time.

        // Most methods using the handle should use GC.KeepAlive(this) to avoid potential handle recycling
        // attacks (i.e. if the WeakReference instance is finalized away underneath you when you're still
        // handling a cached value of the handle then the handle could be freed and reused).
        private nint _taggedHandle;

        // Creates a new WeakReference that keeps track of target.
        // Assumes a Short Weak Reference (ie TrackResurrection is false.)
        public WeakReference(T target)
            : this(target, false)
        {
        }

        // Creates a new WeakReference that keeps track of target.
        //
        public WeakReference(T target, bool trackResurrection)
        {
            Create(target, trackResurrection);
        }

        private WeakReference(SerializationInfo info, StreamingContext context)
        {
            ArgumentNullException.ThrowIfNull(info);

            T target = (T)info.GetValue("TrackedObject", typeof(T))!; // Do not rename (binary serialization)
            bool trackResurrection = info.GetBoolean("TrackResurrection"); // Do not rename (binary serialization)

            Create(target, trackResurrection);
        }

        //
        // We are exposing TryGetTarget instead of a simple getter to avoid a common problem where people write incorrect code like:
        //
        //      WeakReference ref = ...;
        //      if (ref.Target != null)
        //          DoSomething(ref.Target)
        //
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool TryGetTarget([MaybeNullWhen(false), NotNullWhen(true)] out T target)
        {
            T? o = this.Target;
            target = o!;
            return o != null;
        }

        [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            ArgumentNullException.ThrowIfNull(info);

            info.AddValue("TrackedObject", this.Target, typeof(T)); // Do not rename (binary serialization)
            info.AddValue("TrackResurrection", IsTrackResurrection()); // Do not rename (binary serialization)
        }

        // Returns a boolean indicating whether or not we're tracking objects until they're collected (true)
        // or just until they're finalized (false).
        private bool IsTrackResurrection() => (_taggedHandle & TracksResurrectionBit) != 0;

        // Creates a new WeakReference that keeps track of target.
        private void Create(T target, bool trackResurrection)
        {
            nint h = GCHandle.InternalAlloc(target, trackResurrection ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
            _taggedHandle = trackResurrection ?
                h | TracksResurrectionBit :
                h;

#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
            ComAwareWeakReference.ComInfo? comInfo = ComAwareWeakReference.ComInfo.FromObject(target);
            if (comInfo != null)
            {
                ComAwareWeakReference.SetComInfoInConstructor(ref _taggedHandle, comInfo);
            }
#endif
        }

        public void SetTarget(T target)
        {
            nint th = _taggedHandle & ~TracksResurrectionBit;

            // Should only happen for corner cases, like using a WeakReference from a finalizer.
            // GC can finalize the instance if it becomes F-Reachable.
            // That, however, cannot happen while we use the instance.
            if (th == 0)
                throw new InvalidOperationException(SR.InvalidOperation_HandleIsNotInitialized);

#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
            var comInfo = ComAwareWeakReference.ComInfo.FromObject(target);
            if ((th & ComAwareBit) != 0 || comInfo != null)
            {
                ComAwareWeakReference.SetTarget(ref _taggedHandle, target, comInfo);

                // must keep the instance alive as long as we use the handle.
                GC.KeepAlive(this);

                return;
            }
#endif

            GCHandle.InternalSet(th, target);

            // must keep the instance alive as long as we use the handle.
            GC.KeepAlive(this);
        }

        private T? Target
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                nint th = _taggedHandle & ~TracksResurrectionBit;

                // Should only happen for corner cases, like using a WeakReference from a finalizer.
                // GC can finalize the instance if it becomes F-Reachable.
                // That, however, cannot happen while we use the instance.
                if (th == 0)
                    return default;

                T? target;

#if FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
                if ((th & ComAwareBit) != 0)
                {
                    ComAwareWeakReference cwr = ComAwareWeakReference.GetFromTaggedReference(th);

                    target = Unsafe.As<T>(cwr.Target) ?? cwr.RehydrateTarget<T>();

                    // must keep the instance alive as long as we use the handle.
                    GC.KeepAlive(this);

                    return target;
                }
#endif

                // unsafe cast is ok as the handle cannot be destroyed and recycled while we keep the instance alive
#if FEATURE_JAVAMARSHAL
                target = Unsafe.As<T>(GCHandle.InternalGetBridgeWait(th));
#else
                target = Unsafe.As<T>(GCHandle.InternalGet(th));
#endif

                // must keep the instance alive as long as we use the handle.
                GC.KeepAlive(this);

                return target;
            }
        }

        // Note: While WeakReference<T> is formally a finalizable type, the finalizer does not actually run.
        //       Instead the instances are treated specially in GC when scanning for no longer strongly-reachable
        //       finalizable objects.
#pragma warning disable CA1821 // Remove empty Finalizers
        ~WeakReference()
        {
            Debug.Fail(" WeakReference<T> finalizer should never run");
        }
#pragma warning restore CA1821 // Remove empty Finalizers

    }
}