File: src\libraries\System.Private.CoreLib\src\System\Runtime\InteropServices\PinnedGCHandle.T.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\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.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
 
namespace System.Runtime.InteropServices
{
    /// <summary>
    /// Represents a strongly-typed GC handle to a managed object.
    /// The object is pinned at fixed location in GC heap and allows its
    /// address to be taken.
    /// </summary>
    /// <remarks>
    /// <para>This type corresponds to <see cref="GCHandleType.Pinned"/>.</para>
    /// <para>
    /// This type is unsafe if used incorrectly. Incorrect usage like mismanagement
    /// of lifetime, providing invalid handle value or concurrent disposal can result
    /// in hard to diagnose crashes or data corruptions.
    /// </para>
    /// </remarks>
    /// <seealso cref="GCHandle" />
    /// <typeparam name="T">The type of the object this <see cref="PinnedGCHandle{T}"/> tracks to.</typeparam>
    public struct PinnedGCHandle<T> : IEquatable<PinnedGCHandle<T>>, IDisposable
        where T : class?
    {
        // The actual integer handle value that the EE uses internally.
        private IntPtr _handle;
 
        /// <summary>
        /// Allocates a handle for the specified object.
        /// </summary>
        /// <param name="target">The object that uses the <see cref="PinnedGCHandle{T}"/>.</param>
        public PinnedGCHandle(T target)
        {
            // Unlike GCHandle, pinning any object is allowed
            _handle = GCHandle.InternalAlloc(target, GCHandleType.Pinned);
        }
 
        private PinnedGCHandle(IntPtr handle) => _handle = handle;
 
        /// <summary>Determine whether this handle has been allocated or not.</summary>
        public readonly bool IsAllocated => _handle != IntPtr.Zero;
 
        /// <summary>Gets or sets the object this handle represents.</summary>
        /// <exception cref="NullReferenceException">If the handle is not initialized or already disposed.</exception>
        public readonly T Target
        {
            get
            {
                IntPtr handle = _handle;
                GCHandle.CheckUninitialized(handle);
                // Skip the type check to provide lowest overhead.
                return Unsafe.As<T>(GCHandle.InternalGet(handle)!);
            }
            set
            {
                IntPtr handle = _handle;
                GCHandle.CheckUninitialized(handle);
                // Unlike GCHandle, pinning any object is allowed
                GCHandle.InternalSet(handle, value);
            }
        }
 
        /// <summary>
        /// Retrieves the address of object data in a <see cref="PinnedGCHandle{T}"/>.
        /// </summary>
        /// <returns>
        /// The address of first instance field of the pinned object,
        /// or <see langword="null"/> if the handle doesn't point to any object.
        /// </returns>
        /// <remarks>
        /// <para>
        /// This method is intended to be used with types other than array or <see cref="string"/>.
        /// For array or <see cref="string"/>, use <see cref="GCHandleExtensions.GetAddressOfArrayData{T}(PinnedGCHandle{T[]})"/>
        /// or <see cref="GCHandleExtensions.GetAddressOfStringData(PinnedGCHandle{string})"/> instead.
        /// </para>
        /// <para>
        /// This method should only be used for blittable types.
        /// </para>
        /// </remarks>
        /// <exception cref="NullReferenceException">If the handle is not initialized or already disposed.</exception>
        [CLSCompliant(false)]
        public readonly unsafe void* GetAddressOfObjectData()
        {
            object? target = Target;
            if (target is null)
            {
                return null;
            }
 
            // Unsafe.AsPointer is safe since object is pinned.
            return Unsafe.AsPointer(ref target.GetRawData());
        }
 
        /// <summary>
        /// Returns a new <see cref="PinnedGCHandle{T}"/> object created from a handle to a managed object.
        /// </summary>
        /// <param name="value">An <see cref="IntPtr"/> handle to a managed object to create a <see cref="PinnedGCHandle{T}"/> object from.</param>
        /// <returns>A new <see cref="PinnedGCHandle{T}"/> object that corresponds to the value parameter.</returns>
        /// <remarks>
        /// <para>This method doesn't validate the provided handle value. The caller must ensure the validity of the handle.</para>
        /// <para>
        /// The <see cref="IntPtr"/> representation of <see cref="PinnedGCHandle{T}"/> is not
        /// interchangable with <see cref="GCHandle"/>.
        /// </para>
        /// </remarks>
        public static PinnedGCHandle<T> FromIntPtr(IntPtr value) => new PinnedGCHandle<T>(value);
 
        /// <summary>
        /// Returns the internal integer representation of a <see cref="PinnedGCHandle{T}"/> object.
        /// </summary>
        /// <param name="value">A <see cref="PinnedGCHandle{T}"/> object to retrieve an internal integer representation from.</param>
        /// <returns>An <see cref="IntPtr"/> object that represents a <see cref="PinnedGCHandle{T}"/> object.</returns>
        /// <remarks>
        /// The <see cref="IntPtr"/> representation of <see cref="PinnedGCHandle{T}"/> is not
        /// interchangable with <see cref="GCHandle"/>.
        /// </remarks>
        public static IntPtr ToIntPtr(PinnedGCHandle<T> value) => value._handle;
 
        /// <summary>Releases this <see cref="PinnedGCHandle{T}"/>.</summary>
        /// <remarks>This method is not thread safe.</remarks>
        public void Dispose()
        {
            // Free the handle if it hasn't already been freed.
            // Unlike GCHandle.Free, no thread safety is provided.
            IntPtr handle = _handle;
            if (handle != IntPtr.Zero)
            {
                _handle = IntPtr.Zero;
                GCHandle.InternalFree(handle);
            }
        }
 
        /// <inheritdoc/>
        public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is PinnedGCHandle<T> handle && Equals(handle);
 
        /// <inheritdoc cref="IEquatable{T}.Equals(T)"/>
        public readonly bool Equals(PinnedGCHandle<T> other) => _handle == other._handle;
 
        /// <summary>
        /// Returns the hash code for the current instance.
        /// </summary>
        /// <returns>A hash code for the current instance.</returns>
        public override readonly int GetHashCode() => _handle.GetHashCode();
    }
}