.NET-Community-Toolkit/CommunityToolkit.HighPerfor.../NullableReadOnlyRef{T}.cs

142 lines
5.4 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#if NETSTANDARD2_1_OR_GREATER
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace CommunityToolkit.HighPerformance;
/// <summary>
/// A <see langword="struct"/> that can store an optional readonly reference to a value of a specified type.
/// </summary>
/// <typeparam name="T">The type of value to reference.</typeparam>
[RequiresPreviewFeatures(
"The NullableReadOnlyRef<T> type has no compiler support to ensure the lifetime of referenced values is respected, and as such using it incorrectly may lead to GC holes.",
Url = "https://github.com/dotnet/runtime/issues/46104")]
public readonly ref struct NullableReadOnlyRef<T>
{
/// <summary>
/// The 1-length <see cref="ReadOnlySpan{T}"/> instance used to track the target <typeparamref name="T"/> value.
/// </summary>
private readonly ReadOnlySpan<T> span;
/// <summary>
/// Initializes a new instance of the <see cref="NullableReadOnlyRef{T}"/> struct.
/// </summary>
/// <param name="value">The readonly reference to the target <typeparamref name="T"/> value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NullableReadOnlyRef(in T value)
{
ref T r0 = ref Unsafe.AsRef(value);
this.span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1);
}
/// <summary>
/// Initializes a new instance of the <see cref="NullableReadOnlyRef{T}"/> struct.
/// </summary>
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> instance to track the target <typeparamref name="T"/> reference.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private NullableReadOnlyRef(ReadOnlySpan<T> span)
{
this.span = span;
}
/// <summary>
/// Gets a <see cref="NullableReadOnlyRef{T}"/> instance representing a <see langword="null"/> reference.
/// </summary>
public static NullableReadOnlyRef<T> Null
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => default;
}
/// <summary>
/// Gets a value indicating whether or not the current <see cref="NullableReadOnlyRef{T}"/> instance wraps a valid reference that can be accessed.
/// </summary>
public unsafe bool HasValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
// See comment in NullableRef<T> about this
byte length = unchecked((byte)this.span.Length);
return *(bool*)&length;
}
}
/// <summary>
/// Gets the <typeparamref name="T"/> reference represented by the current <see cref="NullableReadOnlyRef{T}"/> instance.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if <see cref="HasValue"/> is <see langword="false"/>.</exception>
public ref readonly T Value
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
if (!HasValue)
{
ThrowInvalidOperationException();
}
return ref MemoryMarshal.GetReference(this.span);
}
}
/// <summary>
/// Implicitly converts a <see cref="Ref{T}"/> instance into a <see cref="NullableReadOnlyRef{T}"/> one.
/// </summary>
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableReadOnlyRef<T>(Ref<T> reference)
{
return new(reference.Span);
}
/// <summary>
/// Implicitly converts a <see cref="ReadOnlyRef{T}"/> instance into a <see cref="NullableReadOnlyRef{T}"/> one.
/// </summary>
/// <param name="reference">The input <see cref="ReadOnlyRef{T}"/> instance.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableReadOnlyRef<T>(ReadOnlyRef<T> reference)
{
return new(reference.Span);
}
/// <summary>
/// Implicitly converts a <see cref="NullableRef{T}"/> instance into a <see cref="NullableReadOnlyRef{T}"/> one.
/// </summary>
/// <param name="reference">The input <see cref="Ref{T}"/> instance.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator NullableReadOnlyRef<T>(NullableRef<T> reference)
{
return new(reference.Span);
}
/// <summary>
/// Explicitly gets the <typeparamref name="T"/> value from a given <see cref="NullableReadOnlyRef{T}"/> instance.
/// </summary>
/// <param name="reference">The input <see cref="NullableReadOnlyRef{T}"/> instance.</param>
/// <exception cref="InvalidOperationException">Thrown if <see cref="HasValue"/> is <see langword="false"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator T(NullableReadOnlyRef<T> reference)
{
return reference.Value;
}
/// <summary>
/// Throws a <see cref="InvalidOperationException"/> when trying to access <see cref="Value"/> for a default instance.
/// </summary>
private static void ThrowInvalidOperationException()
{
throw new InvalidOperationException("The current instance doesn't have a value that can be accessed.");
}
}
#endif