File: src\libraries\System.Private.CoreLib\src\System\Enum.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.
 
// Rare enums types not expressible in C# are not supported in native AOT
#if !NATIVEAOT
#define RARE_ENUMS
#endif
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
#pragma warning disable 8500 // Allow taking address of managed types
 
namespace System
{
    /// <summary>Provides the base class for enumerations.</summary>
    [Serializable]
    [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
    public abstract partial class Enum : ValueType, IComparable, ISpanFormattable, IConvertible
    {
        // Notes:
        // - This implementation includes partial support for float/double/nint/nuint-based enums.
        //   The type loader does not prohibit such enums, and older versions of the ECMA spec include
        //   them as possible enum types. However there are many things broken throughout the stack for
        //   float/double/nint/nuint enums. There was a conscious decision made to not fix the whole stack
        //   to work well for them because the right behavior is often unclear, and it is hard to test and
        //   very low value because such enums cannot be expressed in C# and are very rarely encountered.
        // - To avoid significant code bloat for public generic APIs, we avoid making the whole implementation
        //   generic on the enum type (TEnum) all the way through the implementation.  Instead, at the public
        //   entry point we look up the underlying type for that enum, and use that underlying type as the
        //   generic for the rest of that call tree (TUnderlying).  That type is important for controlling
        //   how the parsing and formatting APIs behave, in particular when parsing and formatting an enum
        //   value as a number.  However, various APIs also need to be able to look up names associated with
        //   values, and those operations assume the values in the underlying storage are sorted, and for
        //   integer underlying types, sorted based on the unsigned representation of the underlying value.
        //   So, for those operations we also or instead pass along a generic for the actual type in which
        //   the data is stored in the underlying values array (TStorage), which will either be the same
        //   as the underlying type if the underlying type is not a signed integer, or will be the unsigned
        //   counterpart to the underlying type if the underlying type is a signed integer.  This also has
        //   the benefit of further limited code bloat, as for the underlying types representable in C#,
        //   we then only need up to 4 instantiations of some code paths rather than 8.
 
        /// <summary>Character used to separate flag enum values when formatted in a list.</summary>
        private const char EnumSeparatorChar = ',';
 
        /// <summary>Retrieves the name of the constant in the specified enumeration type that has the specified value.</summary>
        /// <typeparam name="TEnum">The type of the enumeration.</typeparam>
        /// <param name="value">The value of a particular enumerated constant in terms of its underlying type.</param>
        /// <returns>
        /// A string containing the name of the enumerated constant in <typeparamref name="TEnum"/> whose value is <paramref name="value"/>,
        /// or <see langword="null"/> if no such constant is found.
        /// </returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe string? GetName<TEnum>(TEnum value) where TEnum : struct, Enum
        {
            RuntimeType rt = (RuntimeType)typeof(TEnum);
            Type underlyingType = typeof(TEnum).GetEnumUnderlyingType();
 
            if (underlyingType == typeof(sbyte) || underlyingType == typeof(byte)) return GetNameInlined(GetEnumInfo<byte>(rt), *(byte*)&value);
            if (underlyingType == typeof(short) || underlyingType == typeof(ushort)) return GetNameInlined(GetEnumInfo<ushort>(rt), *(ushort*)&value);
            if (underlyingType == typeof(int) || underlyingType == typeof(uint)) return GetNameInlined(GetEnumInfo<uint>(rt), *(uint*)&value);
            if (underlyingType == typeof(long) || underlyingType == typeof(ulong)) return GetNameInlined(GetEnumInfo<ulong>(rt), *(ulong*)&value);
#if RARE_ENUMS
            if (underlyingType == typeof(nint) || underlyingType == typeof(nuint)) return GetNameInlined(GetEnumInfo<nuint>(rt), *(nuint*)&value);
            if (underlyingType == typeof(float)) return GetNameInlined(GetEnumInfo<float>(rt), *(float*)&value);
            if (underlyingType == typeof(double)) return GetNameInlined(GetEnumInfo<double>(rt), *(double*)&value);
            if (underlyingType == typeof(char)) return GetNameInlined(GetEnumInfo<char>(rt), *(char*)&value);
#endif
            throw CreateUnknownEnumTypeException();
        }
 
        /// <summary>Retrieves the name of the constant in the specified enumeration type that has the specified value.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="value">The value of a particular enumerated constant in terms of its underlying type.</param>
        /// <returns>
        /// A string containing the name of the enumerated constant in <paramref name="enumType"/> whose value is <paramref name="value"/>,
        /// or <see langword="null"/> if no such constant is found.
        /// </returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> or <paramref name="value"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="enumType"/> is not an <see cref="Enum"/>, or <paramref name="value"/> is neither of type <paramref name="enumType"/>
        /// nor does it have the same underlying type as <paramref name="enumType"/>.
        /// </exception>
        public static string? GetName(Type enumType, object value)
        {
            ArgumentNullException.ThrowIfNull(enumType);
            return enumType.GetEnumName(value);
        }
 
        /// <summary>Retrieves the name of the constant in the specified enumeration type that has the specified value.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="uint64Value">The value of a particular enumerated constant in terms of its underlying type, cast to a <see cref="ulong"/>.</param>
        /// <returns>
        /// A string containing the name of the enumerated constant in <paramref name="enumType"/> whose value is <paramref name="uint64Value"/>,
        /// or <see langword="null"/> if no such constant is found.
        /// </returns>
        internal static string? GetName(RuntimeType enumType, ulong uint64Value)
        {
            // For a given underlying type, validate that the specified ulong is in the range
            // of that type.  If it's not, it definitely doesn't match.  If it is, delegate
            // to GetName<TUnderlyingType> to look it up.
            Type underlyingType = enumType.GetEnumUnderlyingType();
            switch (Type.GetTypeCode(underlyingType)) // can't use InternalGetCorElementType as enumType may actually be the underlying type
            {
                case TypeCode.SByte:
                    if ((long)uint64Value < sbyte.MinValue || (long)uint64Value > sbyte.MaxValue) return null;
                    return GetName(GetEnumInfo<byte>(enumType), (byte)(sbyte)uint64Value);
 
                case TypeCode.Byte:
                    if (uint64Value > byte.MaxValue) return null;
                    return GetName(GetEnumInfo<byte>(enumType), (byte)uint64Value);
 
                case TypeCode.Int16:
                    if ((long)uint64Value < short.MinValue || (long)uint64Value > short.MaxValue) return null;
                    return GetName(GetEnumInfo<ushort>(enumType), (ushort)(short)uint64Value);
 
                case TypeCode.UInt16:
                    if (uint64Value > ushort.MaxValue) return null;
                    return GetName(GetEnumInfo<ushort>(enumType), (ushort)uint64Value);
 
                case TypeCode.Int32:
                    if ((long)uint64Value < int.MinValue || (long)uint64Value > int.MaxValue) return null;
                    return GetName(GetEnumInfo<uint>(enumType), (uint)(int)uint64Value);
 
                case TypeCode.UInt32:
                    if (uint64Value > uint.MaxValue) return null;
                    return GetName(GetEnumInfo<uint>(enumType), (uint)uint64Value);
 
                case TypeCode.Int64:
                    return GetName(GetEnumInfo<ulong>(enumType), (ulong)(long)uint64Value);
 
                case TypeCode.UInt64:
                    return GetName(GetEnumInfo<ulong>(enumType), uint64Value);
 
#if RARE_ENUMS
                case TypeCode.Char:
                    if (uint64Value > char.MaxValue) return null;
                    return GetName(GetEnumInfo<char>(enumType), (char)uint64Value);
#endif
            };
 
#if RARE_ENUMS
            if (underlyingType == typeof(nint))
            {
                if ((long)uint64Value < nint.MinValue || (long)uint64Value > nint.MaxValue) return null;
                return GetName(GetEnumInfo<nuint>(enumType), (nuint)(nint)uint64Value);
            }
 
            if (underlyingType == typeof(nuint))
            {
                if (uint64Value > nuint.MaxValue) return null;
                return GetName(GetEnumInfo<nuint>(enumType), (nuint)uint64Value);
            }
#endif
 
            throw CreateUnknownEnumTypeException();
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static string? GetName<TStorage>(EnumInfo<TStorage> enumInfo, TStorage value)
            where TStorage : struct, INumber<TStorage> =>
            GetNameInlined(enumInfo, value);
 
        /// <summary>Look up the name for the specified underlying value using the cached <see cref="EnumInfo{TStorage}"/> for the associated enum.</summary>
        /// <typeparam name="TStorage">The type stored in the EnumInfo.  This will either be the same as the underlying type for the enum or its unsigned counterpart if the underlying type is a signed integer.</typeparam>
        /// <param name="enumInfo">The cached <see cref="EnumInfo{TStorage}"/> for the enum type.</param>
        /// <param name="value">The underlying value for which we're searching.</param>
        /// <returns>The name of the value if found; otherwise, <see langword="null"/>.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static string? GetNameInlined<TStorage>(EnumInfo<TStorage> enumInfo, TStorage value)
            where TStorage : struct, INumber<TStorage>
        {
            string[] names = enumInfo.Names;
 
            // If the values are known to be sequential starting from 0, then we can simply compare the value
            // against the length of the array.  The value matches iff it's in-bounds, and if it is, the value
            // in the array is where the corresponding name is stored.
            if (enumInfo.ValuesAreSequentialFromZero)
            {
                if (Unsafe.SizeOf<TStorage>() <= sizeof(uint))
                {
                    // Special-case types types that are <= sizeof(int), as we can then eliminate a bounds check on the array.
                    uint uint32Value = uint.CreateTruncating(value);
                    if (uint32Value < (uint)names.Length)
                    {
                        return names[uint32Value];
                    }
                }
                else
                {
                    // Handle the remaining types.
                    if (ulong.CreateTruncating(value) < (ulong)names.Length)
                    {
                        return names[uint.CreateTruncating(value)];
                    }
                }
            }
            else
            {
                // Search for the value in the array of values. If we find a non-negative index,
                // that's the location of the corresponding name in the names array.
                int index = FindDefinedIndex(enumInfo.Values, value);
                if ((uint)index < (uint)names.Length)
                {
                    return names[index];
                }
            }
 
            // Return null so the caller knows no individual named value could be found.
            return null;
        }
 
        /// <summary>Retrieves an array of the names of the constants in a specified enumeration type.</summary>
        /// <typeparam name="TEnum">The type of the enumeration.</typeparam>
        /// <returns>A string array of the names of the constants in <typeparamref name="TEnum"/>.</returns>
        public static string[] GetNames<TEnum>() where TEnum : struct, Enum
        {
            string[] names;
 
            RuntimeType rt = (RuntimeType)typeof(TEnum);
            Type underlyingType = typeof(TEnum).GetEnumUnderlyingType();
 
            // Get the cached names array.
            if (underlyingType == typeof(sbyte) || underlyingType == typeof(byte)) names = GetEnumInfo<byte>(rt).Names;
            else if (underlyingType == typeof(short) || underlyingType == typeof(ushort)) names = GetEnumInfo<ushort>(rt).Names;
            else if (underlyingType == typeof(int) || underlyingType == typeof(uint)) names = GetEnumInfo<uint>(rt).Names;
            else if (underlyingType == typeof(long) || underlyingType == typeof(ulong)) names = GetEnumInfo<ulong>(rt).Names;
#if RARE_ENUMS
            else if (underlyingType == typeof(nint) || underlyingType == typeof(nuint)) names = GetEnumInfo<nuint>(rt).Names;
            else if (underlyingType == typeof(float)) names = GetEnumInfo<float>(rt).Names;
            else if (underlyingType == typeof(double)) names = GetEnumInfo<double>(rt).Names;
            else if (underlyingType == typeof(char)) names = GetEnumInfo<char>(rt).Names;
#endif
            else throw CreateUnknownEnumTypeException();
 
            // Return a clone of the array to avoid exposing the cached array instance.
            return new ReadOnlySpan<string>(names).ToArray();
        }
 
        /// <summary>Retrieves an array of the names of the constants in a specified enumeration.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <returns>A string array of the names of the constants in <paramref name="enumType"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        public static string[] GetNames(Type enumType)
        {
            ArgumentNullException.ThrowIfNull(enumType);
            return enumType.GetEnumNames();
        }
 
        /// <summary>Gets the cached names array for the specified enum type, without making a copy.</summary>
        /// <remarks>The returned array should not be exposed outside of this assembly.</remarks>
        internal static string[] GetNamesNoCopy(RuntimeType enumType)
        {
            Debug.Assert(enumType.IsActualEnum);
 
            return InternalGetCorElementType(enumType) switch
            {
                CorElementType.ELEMENT_TYPE_I1 or CorElementType.ELEMENT_TYPE_U1 => GetEnumInfo<byte>(enumType).Names,
                CorElementType.ELEMENT_TYPE_I2 or CorElementType.ELEMENT_TYPE_U2 => GetEnumInfo<ushort>(enumType).Names,
                CorElementType.ELEMENT_TYPE_I4 or CorElementType.ELEMENT_TYPE_U4 => GetEnumInfo<uint>(enumType).Names,
                CorElementType.ELEMENT_TYPE_I8 or CorElementType.ELEMENT_TYPE_U8 => GetEnumInfo<ulong>(enumType).Names,
#if RARE_ENUMS
                CorElementType.ELEMENT_TYPE_I or CorElementType.ELEMENT_TYPE_U => GetEnumInfo<nuint>(enumType).Names,
                CorElementType.ELEMENT_TYPE_R4 => GetEnumInfo<float>(enumType).Names,
                CorElementType.ELEMENT_TYPE_R8 => GetEnumInfo<double>(enumType).Names,
                CorElementType.ELEMENT_TYPE_CHAR => GetEnumInfo<char>(enumType).Names,
#endif
                _ => throw CreateUnknownEnumTypeException(),
            };
        }
 
        /// <summary>Returns the underlying type of the specified enumeration.</summary>
        /// <param name="enumType">The enumeration whose underlying type will be retrieved.</param>
        /// <returns>The underlying type of <paramref name="enumType"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        public static Type GetUnderlyingType(Type enumType)
        {
            ArgumentNullException.ThrowIfNull(enumType);
            return enumType.GetEnumUnderlyingType();
        }
 
        /// <summary>Retrieves an array of the values of the constants in a specified enumeration type.</summary>
        /// <typeparam name="TEnum">The type of the enumeration.</typeparam>
        /// <returns>An array that contains the values of the constants in <typeparamref name="TEnum"/>.</returns>
        public static TEnum[] GetValues<TEnum>() where TEnum : struct, Enum
        {
            Array values = GetValuesAsUnderlyingTypeNoCopy((RuntimeType)typeof(TEnum));
            var result = new TEnum[values.Length];
            Array.Copy(values, result, values.Length);
            return result;
        }
 
        /// <summary>Retrieves an array of the values of the constants in a specified enumeration.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <returns>An array that contains the values of the constants in <paramref name="enumType"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        /// <exception cref="InvalidOperationException">
        /// The method is invoked by reflection in a reflection-only context, or <paramref name="enumType"/> is a type from an assembly loaded in a reflection-only context.
        /// </exception>
        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload or the GetValuesAsUnderlyingType method instead.")]
        public static Array GetValues(Type enumType)
        {
            ArgumentNullException.ThrowIfNull(enumType);
            return enumType.GetEnumValues();
        }
 
        /// <summary>Retrieves an array of the values of the underlying type constants in a specified enumeration type.</summary>
        /// <typeparam name="TEnum">An enumeration type.</typeparam>
        /// <remarks>
        /// You can use this method to get enumeration values when it's hard to create an array of the enumeration type.
        /// For example, you might use this method for the <see cref="T:System.Reflection.MetadataLoadContext" /> enumeration or on a platform where run-time code generation is not available.
        /// </remarks>
        /// <returns>An array that contains the values of the underlying type constants in <typeparamref name="TEnum" />.</returns>
        public static Array GetValuesAsUnderlyingType<TEnum>() where TEnum : struct, Enum =>
            typeof(TEnum).GetEnumValuesAsUnderlyingType();
 
        /// <summary>Retrieves an array of the values of the underlying type constants in a specified enumeration.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <remarks>
        /// You can use this method to get enumeration values when it's hard to create an array of the enumeration type.
        /// For example, you might use this method for the MetadataLoadContext enumeration or on a platform where run-time code generation is not available.
        /// </remarks>
        /// <returns>An array that contains the values of the underlying type constants in  <paramref name="enumType" />.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType" /> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType" /> is not an enumeration type.</exception>
        public static Array GetValuesAsUnderlyingType(Type enumType)
        {
            ArgumentNullException.ThrowIfNull(enumType);
            return enumType.GetEnumValuesAsUnderlyingType();
        }
 
        /// <summary>Retrieves an array of the values of the underlying type constants in a specified enumeration type.</summary>
        internal static Array GetValuesAsUnderlyingType(RuntimeType enumType)
        {
            Debug.Assert(enumType.IsActualEnum);
 
            return InternalGetCorElementType(enumType) switch
            {
                CorElementType.ELEMENT_TYPE_I1 => GetEnumInfo<byte>(enumType, getNames: false).CloneValues<sbyte>(),
                CorElementType.ELEMENT_TYPE_U1 => GetEnumInfo<byte>(enumType, getNames: false).CloneValues<byte>(),
                CorElementType.ELEMENT_TYPE_I2 => GetEnumInfo<ushort>(enumType, getNames: false).CloneValues<short>(),
                CorElementType.ELEMENT_TYPE_U2 => GetEnumInfo<ushort>(enumType, getNames: false).CloneValues<ushort>(),
                CorElementType.ELEMENT_TYPE_I4 => GetEnumInfo<uint>(enumType, getNames: false).CloneValues<int>(),
                CorElementType.ELEMENT_TYPE_U4 => GetEnumInfo<uint>(enumType, getNames: false).CloneValues<uint>(),
                CorElementType.ELEMENT_TYPE_I8 => GetEnumInfo<ulong>(enumType, getNames: false).CloneValues<long>(),
                CorElementType.ELEMENT_TYPE_U8 => GetEnumInfo<ulong>(enumType, getNames: false).CloneValues<ulong>(),
#if RARE_ENUMS
                CorElementType.ELEMENT_TYPE_I => GetEnumInfo<nuint>(enumType, getNames: false).CloneValues<nint>(),
                CorElementType.ELEMENT_TYPE_U => GetEnumInfo<nuint>(enumType, getNames: false).CloneValues<nuint>(),
                CorElementType.ELEMENT_TYPE_R4 => GetEnumInfo<float>(enumType, getNames: false).CloneValues<float>(),
                CorElementType.ELEMENT_TYPE_R8 => GetEnumInfo<double>(enumType, getNames: false).CloneValues<double>(),
                CorElementType.ELEMENT_TYPE_CHAR => GetEnumInfo<char>(enumType, getNames: false).CloneValues<char>(),
#endif
                _ => throw CreateUnknownEnumTypeException(),
            };
        }
 
        /// <summary>Retrieves the cached array of the values of the underlying type constants in a specified enumeration.</summary>
        /// <remarks>
        /// The returned array should not be exposed outside of this assembly.
        /// It also may be using the corresponding unsigned type if the actual underlying type was signed.
        /// </remarks>
        internal static Array GetValuesAsUnderlyingTypeNoCopy(RuntimeType enumType)
        {
            Debug.Assert(enumType.IsActualEnum);
 
            return InternalGetCorElementType(enumType) switch
            {
                CorElementType.ELEMENT_TYPE_I1 => GetEnumInfo<byte>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_U1 => GetEnumInfo<byte>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_I2 => GetEnumInfo<ushort>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_U2 => GetEnumInfo<ushort>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_I4 => GetEnumInfo<uint>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_U4 => GetEnumInfo<uint>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_I8 => GetEnumInfo<ulong>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_U8 => GetEnumInfo<ulong>(enumType, getNames: false).Values,
#if RARE_ENUMS
                CorElementType.ELEMENT_TYPE_I => GetEnumInfo<nuint>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_U => GetEnumInfo<nuint>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_R4 => GetEnumInfo<float>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_R8 => GetEnumInfo<double>(enumType, getNames: false).Values,
                CorElementType.ELEMENT_TYPE_CHAR => GetEnumInfo<char>(enumType, getNames: false).Values,
#endif
                _ => throw CreateUnknownEnumTypeException(),
            };
        }
 
        /// <summary>Determines whether one or more bit fields are set in the current instance.</summary>
        /// <param name="flag">An enumeration value.</param>
        /// <returns><see langword="true"/> if the bit field or bit fields that are set in flag are also set in the current instance; otherwise, <see langword="false"/>.</returns>
        /// <exception cref="ArgumentException"><paramref name="flag"/> is a different type than the current instance.</exception>
        [Intrinsic]
        public bool HasFlag(Enum flag)
        {
            ArgumentNullException.ThrowIfNull(flag);
 
            if (GetType() != flag.GetType() && !GetType().IsEquivalentTo(flag.GetType()))
                throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), GetType()));
 
            ref byte pThisValue = ref this.GetRawData();
            ref byte pFlagsValue = ref flag.GetRawData();
 
            switch (InternalGetCorElementType())
            {
                case CorElementType.ELEMENT_TYPE_I1:
                case CorElementType.ELEMENT_TYPE_U1:
                    {
                        byte flagsValue = pFlagsValue;
                        return (pThisValue & flagsValue) == flagsValue;
                    }
 
                case CorElementType.ELEMENT_TYPE_I2:
                case CorElementType.ELEMENT_TYPE_U2:
                    {
                        ushort flagsValue = Unsafe.As<byte, ushort>(ref pFlagsValue);
                        return (Unsafe.As<byte, ushort>(ref pThisValue) & flagsValue) == flagsValue;
                    }
 
                case CorElementType.ELEMENT_TYPE_I4:
                case CorElementType.ELEMENT_TYPE_U4:
                    {
                        uint flagsValue = Unsafe.As<byte, uint>(ref pFlagsValue);
                        return (Unsafe.As<byte, uint>(ref pThisValue) & flagsValue) == flagsValue;
                    }
 
                case CorElementType.ELEMENT_TYPE_I8:
                case CorElementType.ELEMENT_TYPE_U8:
                    {
                        ulong flagsValue = Unsafe.As<byte, ulong>(ref pFlagsValue);
                        return (Unsafe.As<byte, ulong>(ref pThisValue) & flagsValue) == flagsValue;
                    }
 
#if RARE_ENUMS
                case CorElementType.ELEMENT_TYPE_BOOLEAN:
                    goto case CorElementType.ELEMENT_TYPE_U1;
 
                case CorElementType.ELEMENT_TYPE_CHAR:
                    goto case CorElementType.ELEMENT_TYPE_U2;
 
                case CorElementType.ELEMENT_TYPE_R4:
                    goto case CorElementType.ELEMENT_TYPE_U4;
 
                case CorElementType.ELEMENT_TYPE_R8:
                    goto case CorElementType.ELEMENT_TYPE_U8;
 
                case CorElementType.ELEMENT_TYPE_I:
                case CorElementType.ELEMENT_TYPE_U:
#if TARGET_32BIT
                    goto case CorElementType.ELEMENT_TYPE_U4;
#else
                    goto case CorElementType.ELEMENT_TYPE_U8;
#endif
#endif
 
                default:
                    Debug.Fail("Unknown enum underlying type");
                    return false;
            }
        }
 
        /// <summary>Returns a <see cref="bool"/> telling whether a given integral value exists in a specified enumeration.</summary>
        /// <typeparam name="TEnum">The type of the enumeration.</typeparam>
        /// <param name="value">The value in <typeparamref name="TEnum"/>.</param>
        /// <returns><see langword="true"/> if a given integral value exists in a specified enumeration; <see langword="false"/>, otherwise.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe bool IsDefined<TEnum>(TEnum value) where TEnum : struct, Enum
        {
            RuntimeType rt = (RuntimeType)typeof(TEnum);
            Type underlyingType = typeof(TEnum).GetEnumUnderlyingType();
 
            if (underlyingType == typeof(sbyte) || underlyingType == typeof(byte)) return IsDefinedPrimitive(rt, *(byte*)&value);
            if (underlyingType == typeof(short) || underlyingType == typeof(ushort)) return IsDefinedPrimitive(rt, *(ushort*)&value);
            if (underlyingType == typeof(int) || underlyingType == typeof(uint)) return IsDefinedPrimitive(rt, *(uint*)&value);
            if (underlyingType == typeof(long) || underlyingType == typeof(ulong)) return IsDefinedPrimitive(rt, *(ulong*)&value);
#if RARE_ENUMS
            if (underlyingType == typeof(nint) || underlyingType == typeof(nuint)) return IsDefinedPrimitive(rt, *(nuint*)&value);
            if (underlyingType == typeof(float)) return IsDefinedPrimitive(rt, *(float*)&value);
            if (underlyingType == typeof(double)) return IsDefinedPrimitive(rt, *(double*)&value);
            if (underlyingType == typeof(char)) return IsDefinedPrimitive(rt, *(char*)&value);
#endif
 
            throw CreateUnknownEnumTypeException();
        }
 
        /// <summary>Gets whether the specified individual value is defined in the specified enum.</summary>
        internal static bool IsDefinedPrimitive<TStorage>(RuntimeType enumType, TStorage value)
            where TStorage : struct, INumber<TStorage>
        {
            EnumInfo<TStorage> enumInfo = GetEnumInfo<TStorage>(enumType, getNames: false);
            TStorage[] values = enumInfo.Values;
 
            // If the enum's values are all sequentially numbered starting from 0, then we can
            // just return if the requested index is in range.
            if (enumInfo.ValuesAreSequentialFromZero)
            {
                return ulong.CreateTruncating(value) < (ulong)values.Length;
            }
 
            // Otherwise, search for the value.
            return FindDefinedIndex(values, value) >= 0;
        }
 
        /// <summary>Returns a <see cref="bool"/> telling whether a given integral value, or its name as a string, exists in a specified enumeration.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="value">The value or name of a constant in <paramref name="enumType"/>.</param>
        /// <returns><see langword="true"/> if a constant in <paramref name="enumType"/> has a value equal to value; otherwise, <see langword="false"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> or <paramref name="value"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="enumType"/> is not an <see cref="Enum"/>,
        /// or the type of <paramref name="value"/> is an enumeration but it is not an enumeration of type <paramref name="enumType"/>,
        /// or the type of <paramref name="value"/> is not an underlying type of <paramref name="enumType"/>.
        /// </exception>
        public static bool IsDefined(Type enumType, object value)
        {
            ArgumentNullException.ThrowIfNull(enumType);
            return enumType.IsEnumDefined(value);
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static int FindDefinedIndex<TStorage>(TStorage[] values, TStorage value)
            where TStorage : struct, IEquatable<TStorage>, IComparable<TStorage>
        {
            // Binary searching has a higher constant overhead than linear searching.
            // For smaller enums, use IndexOf.
            // For larger enums, use BinarySearch.
            const int NumberOfValuesThreshold = 32; // This threshold can be tweaked over time as optimizations evolve.
            ReadOnlySpan<TStorage> span = values;
            return values.Length <= NumberOfValuesThreshold ?
                span.IndexOf(value) :
                SpanHelpers.BinarySearch(span, value);
        }
 
        /// <summary>Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="value">A string containing the name or value to convert.</param>
        /// <returns>An <see cref="object"/> of type <paramref name="enumType"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is either an empty string or only contains white space.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is a name, but not one of the named constants defined for the enumeration.</exception>
        /// <exception cref="OverflowException"><paramref name="value"/> is outside the range of the underlying type of <paramref name="enumType"/></exception>
        public static object Parse(Type enumType, string value) =>
            Parse(enumType, value, ignoreCase: false);
 
        /// <summary>Converts the span of chars representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.</summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="value">A span containing the name or value to convert.</param>
        /// <returns>An <see cref="object"/> of type <paramref name="enumType"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is either an empty string or only contains white space.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is a name, but not one of the named constants defined for the enumeration.</exception>
        /// <exception cref="OverflowException"><paramref name="value"/> is outside the range of the underlying type of <paramref name="enumType"/></exception>
        public static object Parse(Type enumType, ReadOnlySpan<char> value) =>
            Parse(enumType, value, ignoreCase: false);
 
        /// <summary>
        /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-insensitive.
        /// </summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="value">A string containing the name or value to convert.</param>
        /// <param name="ignoreCase"><see langword="true"/> to ignore case; <see langword="false"/> to regard case.</param>
        /// <returns>An <see cref="object"/> of type <paramref name="enumType"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is either an empty string or only contains white space.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is a name, but not one of the named constants defined for the enumeration.</exception>
        /// <exception cref="OverflowException"><paramref name="value"/> is outside the range of the underlying type of <paramref name="enumType"/></exception>
        public static object Parse(Type enumType, string value, bool ignoreCase)
        {
            if (value is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
            }
 
            bool success = TryParse(enumType, value.AsSpan(), ignoreCase, throwOnFailure: true, out object? result);
            Debug.Assert(success && result is not null);
            return result;
        }
 
        /// <summary>
        /// Converts the span of chars representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-insensitive.
        /// </summary>
        /// <param name="enumType">An enumeration type.</param>
        /// <param name="value">A span containing the name or value to convert.</param>
        /// <param name="ignoreCase"><see langword="true"/> to ignore case; <see langword="false"/> to regard case.</param>
        /// <returns>An <see cref="object"/> of type <paramref name="enumType"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="enumType"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="enumType"/> is not an <see cref="Enum"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is either an empty string or only contains white space.</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> is a name, but not one of the named constants defined for the enumeration.</exception>
        /// <exception cref="OverflowException"><paramref name="value"/> is outside the range of the underlying type of <paramref name="enumType"/></exception>
        public static object Parse(Type enumType, ReadOnlySpan<char> value, bool ignoreCase)
        {
            bool success = TryParse(enumType, value, ignoreCase, throwOnFailure: true, out object? result);
            Debug.Assert(success && result is not null);
            return result;
        }
 
        /// <summary>Converts the string representation of the name or numeric value of one or more enumerated constants specified by <typeparamref name="TEnum"/> to an equivalent enumerated object.</summary>
        /// <typeparam name="TEnum">An enumeration type.</typeparam>
        /// <param name="value">A string containing the name or value to convert.</param>
        /// <returns><typeparamref name="TEnum"/> An object of type <typeparamref name="TEnum"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an <see cref="Enum"/> type</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> does not contain enumeration information</exception>
        public static TEnum Parse<TEnum>(string value) where TEnum : struct =>
            Parse<TEnum>(value, ignoreCase: false);
 
        /// <summary>Converts the span of chars representation of the name or numeric value of one or more enumerated constants specified by <typeparamref name="TEnum"/> to an equivalent enumerated object.</summary>
        /// <typeparam name="TEnum">An enumeration type.</typeparam>
        /// <param name="value">A span containing the name or value to convert.</param>
        /// <returns><typeparamref name="TEnum"/> An object of type <typeparamref name="TEnum"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an <see cref="Enum"/> type</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> does not contain enumeration information</exception>
        public static TEnum Parse<TEnum>(ReadOnlySpan<char> value) where TEnum : struct =>
           Parse<TEnum>(value, ignoreCase: false);
 
        /// <summary>
        /// Converts the string representation of the name or numeric value of one or more enumerated constants specified by <typeparamref name="TEnum"/> to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-insensitive.
        /// </summary>
        /// <typeparam name="TEnum">An enumeration type.</typeparam>
        /// <param name="value">A string containing the name or value to convert.</param>
        /// <param name="ignoreCase"><see langword="true"/> to ignore case; <see langword="false"/> to regard case.</param>
        /// <returns><typeparamref name="TEnum"/> An object of type <typeparamref name="TEnum"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/>.</exception>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an <see cref="Enum"/> type</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> does not contain enumeration information</exception>
        public static TEnum Parse<TEnum>(string value, bool ignoreCase) where TEnum : struct
        {
            if (value is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
            }
 
            bool success = TryParse(value.AsSpan(), ignoreCase, throwOnFailure: true, out TEnum result);
            Debug.Assert(success);
            return result;
        }
 
        /// <summary>
        /// Converts the span of chars representation of the name or numeric value of one or more enumerated constants specified by <typeparamref name="TEnum"/> to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-insensitive.
        /// </summary>
        /// <typeparam name="TEnum">An enumeration type.</typeparam>
        /// <param name="value">A span containing the name or value to convert.</param>
        /// <param name="ignoreCase"><see langword="true"/> to ignore case; <see langword="false"/> to regard case.</param>
        /// <returns><typeparamref name="TEnum"/> An object of type <typeparamref name="TEnum"/> whose value is represented by <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an <see cref="Enum"/> type</exception>
        /// <exception cref="ArgumentException"><paramref name="value"/> does not contain enumeration information</exception>
        public static TEnum Parse<TEnum>(ReadOnlySpan<char> value, bool ignoreCase) where TEnum : struct
        {
            bool success = TryParse(value, ignoreCase, throwOnFailure: true, out TEnum result);
            Debug.Assert(success);
            return result;
        }
 
        /// <summary>Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.</summary>
        /// <param name="enumType">The enum type to use for parsing.</param>
        /// <param name="value">The string representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        public static bool TryParse(Type enumType, string? value, [NotNullWhen(true)] out object? result) =>
            TryParse(enumType, value, ignoreCase: false, out result);
 
        /// <summary>Converts the span of chars representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.</summary>
        /// <param name="enumType">The enum type to use for parsing.</param>
        /// <param name="value">The span representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        public static bool TryParse(Type enumType, ReadOnlySpan<char> value, [NotNullWhen(true)] out object? result) =>
          TryParse(enumType, value, ignoreCase: false, out result);
 
        /// <summary>
        /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-insensitive.
        /// </summary>
        /// <param name="enumType">The enum type to use for parsing.</param>
        /// <param name="value">The string representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="ignoreCase"><see langword="true"/> to read <paramref name="enumType"/> in case insensitive mode; <see langword="false"/> to read <paramref name="enumType"/> in case sensitive mode.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        public static bool TryParse(Type enumType, string? value, bool ignoreCase, [NotNullWhen(true)] out object? result)
        {
            if (value is not null)
            {
                return TryParse(enumType, value.AsSpan(), ignoreCase, throwOnFailure: false, out result);
            }
 
            result = null;
            return false;
        }
 
        /// <summary>
        /// Converts the span of chars representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-insensitive.
        /// </summary>
        /// <param name="enumType">The enum type to use for parsing.</param>
        /// <param name="value">The span representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="ignoreCase"><see langword="true"/> to read <paramref name="enumType"/> in case insensitive mode; <see langword="false"/> to read <paramref name="enumType"/> in case sensitive mode.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        public static bool TryParse(Type enumType, ReadOnlySpan<char> value, bool ignoreCase, [NotNullWhen(true)] out object? result) =>
            TryParse(enumType, value, ignoreCase, throwOnFailure: false, out result);
 
        /// <summary>Core implementation for all non-generic {Try}Parse methods.</summary>
        private static unsafe bool TryParse(Type enumType, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, [NotNullWhen(true)] out object? result)
        {
            bool parsed = false;
            long longScratch = 0;
 
            // Validation on the enum type itself.  Failures here are considered non-parsing failures
            // and thus always throw rather than returning false.
            RuntimeType rt = ValidateRuntimeType(enumType);
 
            switch (InternalGetCorElementType(rt))
            {
                case CorElementType.ELEMENT_TYPE_I1:
                    parsed = TryParseByValueOrName<sbyte, byte>(rt, value, ignoreCase, throwOnFailure, out *(sbyte*)&longScratch);
                    longScratch = *(sbyte*)&longScratch;
                    break;
 
                case CorElementType.ELEMENT_TYPE_U1:
                    parsed = TryParseByValueOrName<byte, byte>(rt, value, ignoreCase, throwOnFailure, out *(byte*)&longScratch);
                    longScratch = *(byte*)&longScratch;
                    break;
 
                case CorElementType.ELEMENT_TYPE_I2:
                    parsed = TryParseByValueOrName<short, ushort>(rt, value, ignoreCase, throwOnFailure, out *(short*)&longScratch);
                    longScratch = *(short*)&longScratch;
                    break;
 
                case CorElementType.ELEMENT_TYPE_U2:
                    parsed = TryParseByValueOrName<ushort, ushort>(rt, value, ignoreCase, throwOnFailure, out *(ushort*)&longScratch);
                    longScratch = *(ushort*)&longScratch;
                    break;
 
                case CorElementType.ELEMENT_TYPE_I4:
                    parsed = TryParseByValueOrName<int, uint>(rt, value, ignoreCase, throwOnFailure, out *(int*)&longScratch);
                    longScratch = *(int*)&longScratch;
                    break;
 
                case CorElementType.ELEMENT_TYPE_U4:
                    parsed = TryParseByValueOrName<uint, uint>(rt, value, ignoreCase, throwOnFailure, out *(uint*)&longScratch);
                    longScratch = *(uint*)&longScratch;
                    break;
 
                case CorElementType.ELEMENT_TYPE_I8:
                    parsed = TryParseByValueOrName<long, ulong>(rt, value, ignoreCase, throwOnFailure, out longScratch);
                    break;
 
                case CorElementType.ELEMENT_TYPE_U8:
                    parsed = TryParseByValueOrName<ulong, ulong>(rt, value, ignoreCase, throwOnFailure, out *(ulong*)&longScratch);
                    break;
 
                default:
                    parsed = TryParseRareTypes(rt, value, ignoreCase, throwOnFailure, out longScratch);
                    break;
            }
 
            result = parsed ? InternalBoxEnum(rt.TypeHandle, longScratch) : null;
            return parsed;
 
            [MethodImpl(MethodImplOptions.NoInlining)]
            static bool TryParseRareTypes(RuntimeType rt, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, [NotNullWhen(true)] out long result)
            {
#if RARE_ENUMS
                bool parsed;
 
                switch (InternalGetCorElementType(rt))
                {
                    case CorElementType.ELEMENT_TYPE_R4:
                        {
                            parsed = TryParseRareTypeByValueOrName<float, float>(rt, value, ignoreCase, throwOnFailure, out float localResult);
                            result = BitConverter.SingleToInt32Bits(localResult);
                        }
                        break;
 
                    case CorElementType.ELEMENT_TYPE_R8:
                        {
                            parsed = TryParseRareTypeByValueOrName<double, double>(rt, value, ignoreCase, throwOnFailure, out double localResult);
                            result = BitConverter.DoubleToInt64Bits(localResult);
                        }
                        break;
 
                    case CorElementType.ELEMENT_TYPE_I:
                        {
                            parsed = TryParseRareTypeByValueOrName<nint, nuint>(rt, value, ignoreCase, throwOnFailure, out nint localResult);
                            result = localResult;
                        }
                        break;
 
                    case CorElementType.ELEMENT_TYPE_U:
                        {
                            parsed = TryParseRareTypeByValueOrName<nuint, nuint>(rt, value, ignoreCase, throwOnFailure, out nuint localResult);
                            result = (long)localResult;
                        }
                        break;
 
                    case CorElementType.ELEMENT_TYPE_CHAR:
                        {
                            parsed = TryParseRareTypeByValueOrName<char, char>(rt, value, ignoreCase, throwOnFailure, out char localResult);
                            result = localResult;
                        }
                        break;
 
                    default:
                        throw CreateUnknownEnumTypeException();
                }
 
                return parsed;
#else
                throw CreateUnknownEnumTypeException();
#endif
            }
        }
 
        /// <summary>Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.</summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="value">The string representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an enumeration type</exception>
        public static bool TryParse<TEnum>([NotNullWhen(true)] string? value, out TEnum result) where TEnum : struct =>
            TryParse(value, ignoreCase: false, out result);
 
        /// <summary>Converts the span of chars representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.</summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="value">The span of chars representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an enumeration type</exception>
        public static bool TryParse<TEnum>(ReadOnlySpan<char> value, out TEnum result) where TEnum : struct =>
            TryParse(value, ignoreCase: false, throwOnFailure: false, out result);
 
        /// <summary>
        /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-sensitive.
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="value">The string representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="ignoreCase"><see langword="true"/> to ignore case; <see langword="false"/> to consider case.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an enumeration type</exception>
        public static bool TryParse<TEnum>([NotNullWhen(true)] string? value, bool ignoreCase, out TEnum result) where TEnum : struct
        {
            if (value is not null)
            {
                return TryParse(value.AsSpan(), ignoreCase, throwOnFailure: false, out result);
            }
 
            result = default;
            return false;
        }
 
        /// <summary>
        /// Converts the span of chars representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// A parameter specifies whether the operation is case-sensitive.
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="value">The span representation of the name or numeric value of one or more enumerated constants.</param>
        /// <param name="ignoreCase"><see langword="true"/> to ignore case; <see langword="false"/> to consider case.</param>
        /// <param name="result">When this method returns <see langword="true"/>, an object containing an enumeration constant representing the parsed value.</param>
        /// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
        /// <exception cref="ArgumentException"><typeparamref name="TEnum"/> is not an enumeration type</exception>
        public static bool TryParse<TEnum>(ReadOnlySpan<char> value, bool ignoreCase, out TEnum result) where TEnum : struct =>
            TryParse(value, ignoreCase, throwOnFailure: false, out result);
 
        /// <summary>Core implementation for all generic {Try}Parse methods.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single call
        private static bool TryParse<TEnum>(ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out TEnum result) where TEnum : struct
        {
            // Validation on the enum type itself.  Failures here are considered non-parsing failures
            // and thus always throw rather than returning false.
            if (!typeof(TEnum).IsEnum) // with IsEnum being an intrinsic, this whole block will be eliminated for all meaningful cases
            {
                throw new ArgumentException(SR.Arg_MustBeEnum, nameof(TEnum));
            }
 
            Unsafe.SkipInit(out result);
            RuntimeType rt = (RuntimeType)typeof(TEnum);
            Type underlyingType = typeof(TEnum).GetEnumUnderlyingType();
 
            if (underlyingType == typeof(sbyte)) return TryParseByValueOrName<sbyte, byte>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, sbyte>(ref result));
            if (underlyingType == typeof(byte)) return TryParseByValueOrName<byte, byte>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, byte>(ref result));
            if (underlyingType == typeof(short)) return TryParseByValueOrName<short, ushort>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, short>(ref result));
            if (underlyingType == typeof(ushort)) return TryParseByValueOrName<ushort, ushort>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, ushort>(ref result));
            if (underlyingType == typeof(int)) return TryParseByValueOrName<int, uint>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, int>(ref result));
            if (underlyingType == typeof(uint)) return TryParseByValueOrName<uint, uint>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, uint>(ref result));
            if (underlyingType == typeof(long)) return TryParseByValueOrName<long, ulong>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, long>(ref result));
            if (underlyingType == typeof(ulong)) return TryParseByValueOrName<ulong, ulong>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, ulong>(ref result));
#if RARE_ENUMS
            if (underlyingType == typeof(nint)) return TryParseRareTypeByValueOrName<nint, nuint>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, nint>(ref result));
            if (underlyingType == typeof(nuint)) return TryParseRareTypeByValueOrName<nuint, nuint>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, nuint>(ref result));
            if (underlyingType == typeof(float)) return TryParseRareTypeByValueOrName<float, float>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, float>(ref result));
            if (underlyingType == typeof(double)) return TryParseRareTypeByValueOrName<double, double>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, double>(ref result));
            if (underlyingType == typeof(char)) return TryParseRareTypeByValueOrName<char, char>(rt, value, ignoreCase, throwOnFailure, out Unsafe.As<TEnum, char>(ref result));
#endif
 
            throw CreateUnknownEnumTypeException();
        }
 
        /// <summary>Core implementation for all {Try}Parse methods, both generic and non-generic, parsing either by value or by name.</summary>
        private static unsafe bool TryParseByValueOrName<TUnderlying, TStorage>(
            RuntimeType enumType, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out TUnderlying result)
            where TUnderlying : unmanaged, IBinaryIntegerParseAndFormatInfo<TUnderlying>
            where TStorage : unmanaged, IBinaryIntegerParseAndFormatInfo<TStorage>
        {
            AssertValidGenerics<TUnderlying, TStorage>();
 
            if (!value.IsEmpty)
            {
                char c = value[0];
                if (char.IsWhiteSpace(c))
                {
                    value = value.TrimStart();
                    if (value.IsEmpty)
                    {
                        goto ParseFailure;
                    }
 
                    c = value[0];
                }
 
                if (!char.IsAsciiDigit(c) && c != '-' && c != '+')
                {
                    Unsafe.SkipInit(out result);
                    return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As<TUnderlying, TStorage>(ref result));
                }
 
                NumberFormatInfo numberFormat = CultureInfo.InvariantCulture.NumberFormat;
                const NumberStyles NumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite;
 
                Number.ParsingStatus status = Number.TryParseBinaryIntegerStyle(value, NumberStyle, numberFormat, out result);
                if (status == Number.ParsingStatus.OK)
                {
                    return true;
                }
 
                if (status != Number.ParsingStatus.Overflow)
                {
                    Unsafe.SkipInit(out result);
                    return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As<TUnderlying, TStorage>(ref result));
                }
 
                if (throwOnFailure)
                {
                    Number.ThrowOverflowException<TUnderlying>();
                }
            }
 
        ParseFailure:
            if (throwOnFailure)
            {
                ThrowInvalidEmptyParseArgument();
            }
 
            result = default;
            return false;
        }
 
        private static unsafe bool TryParseRareTypeByValueOrName<TUnderlying, TStorage>(
            RuntimeType enumType, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out TUnderlying result)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>, IMinMaxValue<TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>, IMinMaxValue<TStorage>
        {
            AssertValidGenerics<TUnderlying, TStorage>();
 
            if (!value.IsEmpty)
            {
                char c = value[0];
                if (char.IsWhiteSpace(c))
                {
                    value = value.TrimStart();
                    if (value.IsEmpty)
                    {
                        goto ParseFailure;
                    }
 
                    c = value[0];
                }
 
                if (!char.IsAsciiDigit(c) && c != '-' && c != '+')
                {
                    Unsafe.SkipInit(out result);
                    return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As<TUnderlying, TStorage>(ref result));
                }
 
#if RARE_ENUMS
                Number.ParsingStatus status;
                Type underlyingType = GetUnderlyingType(enumType);
 
                try
                {
                    result = (TUnderlying)ToObject(enumType, Convert.ChangeType(value.ToString(), underlyingType, CultureInfo.InvariantCulture)!);
                    return true;
                }
                catch (FormatException)
                {
                    status = Number.ParsingStatus.Failed; // e.g. tlbimp enums that can have values of the form "3D"
                }
                catch when (!throwOnFailure)
                {
                    status = Number.ParsingStatus.Overflow; // fall through to returning failure
                }
 
                if (status != Number.ParsingStatus.Overflow)
                {
                    Unsafe.SkipInit(out result);
                    return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As<TUnderlying, TStorage>(ref result));
                }
 
                if (throwOnFailure)
                {
                    ThrowHelper.ThrowOverflowException();
                }
#else
                throw CreateUnknownEnumTypeException();
#endif
            }
 
            ParseFailure:
            if (throwOnFailure)
            {
                ThrowInvalidEmptyParseArgument();
            }
 
            result = default;
            return false;
        }
 
        /// <summary>Handles just the name parsing portion of <see cref="TryParseRareTypeByValueOrName"/>.</summary>
        private static bool TryParseByName<TStorage>(RuntimeType enumType, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out TStorage result)
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>
        {
            ReadOnlySpan<char> originalValue = value;
 
            // Find the field. Let's assume that these are always static classes because the class is an enum.
            EnumInfo<TStorage> enumInfo = GetEnumInfo<TStorage>(enumType);
            string[] enumNames = enumInfo.Names;
            TStorage[] enumValues = enumInfo.Values;
 
            bool parsed = true;
            TStorage localResult = default;
            while (value.Length > 0)
            {
                // Find the next separator.
                ReadOnlySpan<char> subvalue;
                int endIndex = value.IndexOf(EnumSeparatorChar);
                if (endIndex < 0)
                {
                    // No next separator; use the remainder as the next value.
                    subvalue = value.Trim();
                    value = default;
                }
                else if (endIndex != value.Length - 1)
                {
                    // Found a separator before the last char.
                    subvalue = value.Slice(0, endIndex).Trim();
                    value = value.Slice(endIndex + 1);
                }
                else
                {
                    // Last char was a separator, which is invalid.
                    parsed = false;
                    break;
                }
 
                // Try to match this substring against each enum name
                bool success = false;
                if (ignoreCase)
                {
                    for (int i = 0; i < enumNames.Length; i++)
                    {
                        if (subvalue.EqualsOrdinalIgnoreCase(enumNames[i]))
                        {
                            localResult |= enumValues[i];
                            success = true;
                            break;
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < enumNames.Length; i++)
                    {
                        if (subvalue.SequenceEqual(enumNames[i]))
                        {
                            localResult |= enumValues[i];
                            success = true;
                            break;
                        }
                    }
                }
 
                if (!success)
                {
                    parsed = false;
                    break;
                }
            }
 
            if (parsed)
            {
                result = localResult;
                return true;
            }
 
            if (throwOnFailure)
            {
                throw new ArgumentException(SR.Format(SR.Arg_EnumValueNotFound, originalValue.ToString()));
            }
 
            result = default;
            return false;
        }
 
        /// <summary>
        /// Silently convert the <paramref name="value"/> to a <see cref="ulong"/> from the other base types for enum without
        /// throwing an exception (other than for an unknown enum type).
        /// </summary>
        /// <remarks>This is needed since the Convert functions do overflow checks.</remarks>
        internal static ulong ToUInt64(object value)
        {
            switch (Convert.GetTypeCode(value))
            {
                case TypeCode.SByte: return (ulong)(sbyte)value;
                case TypeCode.Byte: return (byte)value;
                case TypeCode.Int16: return (ulong)(short)value;
                case TypeCode.UInt16: return (ushort)value;
                case TypeCode.Int32: return (ulong)(int)value;
                case TypeCode.Int64: return (ulong)(long)value;
                case TypeCode.UInt32: return (uint)value;
                case TypeCode.UInt64: return (ulong)value;
                case TypeCode.Char: return (char)value;
            };
 
            if (value is not null)
            {
                Type valueType = value.GetType();
                if (valueType.IsEnum)
                {
                    valueType = valueType.GetEnumUnderlyingType();
                }
 
                if (valueType == typeof(nint)) return (ulong)(nint)value;
                if (valueType == typeof(nuint)) return (nuint)value;
            }
 
            throw CreateUnknownEnumTypeException();
        }
 
        /// <summary>Gets a boxed underlying value of this enum.</summary>
        internal object GetValue()
        {
            ref byte data = ref this.GetRawData();
            return InternalGetCorElementType() switch
            {
                CorElementType.ELEMENT_TYPE_I1 => Unsafe.As<byte, sbyte>(ref data),
                CorElementType.ELEMENT_TYPE_U1 => data,
                CorElementType.ELEMENT_TYPE_I2 => Unsafe.As<byte, short>(ref data),
                CorElementType.ELEMENT_TYPE_U2 => Unsafe.As<byte, ushort>(ref data),
                CorElementType.ELEMENT_TYPE_I4 => Unsafe.As<byte, int>(ref data),
                CorElementType.ELEMENT_TYPE_U4 => Unsafe.As<byte, uint>(ref data),
                CorElementType.ELEMENT_TYPE_I8 => Unsafe.As<byte, long>(ref data),
                CorElementType.ELEMENT_TYPE_U8 => Unsafe.As<byte, ulong>(ref data),
#if RARE_ENUMS
                CorElementType.ELEMENT_TYPE_R4 => Unsafe.As<byte, float>(ref data),
                CorElementType.ELEMENT_TYPE_R8 => Unsafe.As<byte, double>(ref data),
                CorElementType.ELEMENT_TYPE_I => Unsafe.As<byte, IntPtr>(ref data),
                CorElementType.ELEMENT_TYPE_U => Unsafe.As<byte, UIntPtr>(ref data),
                CorElementType.ELEMENT_TYPE_CHAR => Unsafe.As<byte, char>(ref data),
                CorElementType.ELEMENT_TYPE_BOOLEAN => Unsafe.As<byte, bool>(ref data),
#endif
                _ => throw CreateUnknownEnumTypeException(),
            };
        }
 
        /// <inheritdoc/>
        public override bool Equals([NotNullWhen(true)] object? obj)
        {
            if (obj is null)
                return false;
 
            if (this == obj)
                return true;
 
            if (this.GetType() != obj.GetType())
                return false;
 
            ref byte pThisValue = ref this.GetRawData();
            ref byte pOtherValue = ref obj.GetRawData();
 
            switch (InternalGetCorElementType())
            {
                case CorElementType.ELEMENT_TYPE_I1:
                case CorElementType.ELEMENT_TYPE_U1:
                    return pThisValue == pOtherValue;
 
                case CorElementType.ELEMENT_TYPE_I2:
                case CorElementType.ELEMENT_TYPE_U2:
                    return Unsafe.As<byte, ushort>(ref pThisValue) == Unsafe.As<byte, ushort>(ref pOtherValue);
 
                case CorElementType.ELEMENT_TYPE_I4:
                case CorElementType.ELEMENT_TYPE_U4:
                    return Unsafe.As<byte, uint>(ref pThisValue) == Unsafe.As<byte, uint>(ref pOtherValue);
 
                case CorElementType.ELEMENT_TYPE_I8:
                case CorElementType.ELEMENT_TYPE_U8:
                    return Unsafe.As<byte, ulong>(ref pThisValue) == Unsafe.As<byte, ulong>(ref pOtherValue);
 
#if RARE_ENUMS
                case CorElementType.ELEMENT_TYPE_BOOLEAN:
                    goto case CorElementType.ELEMENT_TYPE_U1;
 
                case CorElementType.ELEMENT_TYPE_CHAR:
                    goto case CorElementType.ELEMENT_TYPE_U2;
 
                case CorElementType.ELEMENT_TYPE_R4:
                    goto case CorElementType.ELEMENT_TYPE_U4;
 
                case CorElementType.ELEMENT_TYPE_R8:
                    goto case CorElementType.ELEMENT_TYPE_U8;
 
                case CorElementType.ELEMENT_TYPE_I:
                case CorElementType.ELEMENT_TYPE_U:
#if TARGET_32BIT
                    goto case CorElementType.ELEMENT_TYPE_U4;
#else
                    goto case CorElementType.ELEMENT_TYPE_U8;
#endif
#endif
 
                default:
                    Debug.Fail("Unknown enum underlying type");
                    return false;
            }
        }
 
        /// <inheritdoc/>
        public override int GetHashCode()
        {
            // CONTRACT with the runtime: GetHashCode of enum types is implemented as GetHashCode of the underlying type.
            // The runtime can bypass calls to Enum::GetHashCode and call the underlying type's GetHashCode directly
            // to avoid boxing the enum.
            ref byte data = ref this.GetRawData();
            return InternalGetCorElementType() switch
            {
                CorElementType.ELEMENT_TYPE_I1 => Unsafe.As<byte, sbyte>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_U1 => data.GetHashCode(),
                CorElementType.ELEMENT_TYPE_I2 => Unsafe.As<byte, short>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_U2 => Unsafe.As<byte, ushort>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_I4 => Unsafe.As<byte, int>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_U4 => Unsafe.As<byte, uint>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_I8 => Unsafe.As<byte, long>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_U8 => Unsafe.As<byte, ulong>(ref data).GetHashCode(),
#if RARE_ENUMS
                CorElementType.ELEMENT_TYPE_R4 => Unsafe.As<byte, float>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_R8 => Unsafe.As<byte, double>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_I => Unsafe.As<byte, IntPtr>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_U => Unsafe.As<byte, UIntPtr>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_CHAR => Unsafe.As<byte, char>(ref data).GetHashCode(),
                CorElementType.ELEMENT_TYPE_BOOLEAN => Unsafe.As<byte, bool>(ref data).GetHashCode(),
#endif
                _ => throw CreateUnknownEnumTypeException(),
            };
        }
 
        /// <inheritdoc/>
        public int CompareTo(object? target)
        {
            if (target == this)
                return 0;
 
            if (target == null)
                return 1; // all values are greater than null
 
            if (GetType() != target.GetType())
                throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, target.GetType(), GetType()));
 
            ref byte pThisValue = ref this.GetRawData();
            ref byte pTargetValue = ref target.GetRawData();
 
            switch (InternalGetCorElementType())
            {
                case CorElementType.ELEMENT_TYPE_I1:
                    return Unsafe.As<byte, sbyte>(ref pThisValue).CompareTo(Unsafe.As<byte, sbyte>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_U1:
                    return pThisValue.CompareTo(pTargetValue);
 
                case CorElementType.ELEMENT_TYPE_I2:
                    return Unsafe.As<byte, short>(ref pThisValue).CompareTo(Unsafe.As<byte, short>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_U2:
                    return Unsafe.As<byte, ushort>(ref pThisValue).CompareTo(Unsafe.As<byte, ushort>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_I4:
                    return Unsafe.As<byte, int>(ref pThisValue).CompareTo(Unsafe.As<byte, int>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_U4:
                    return Unsafe.As<byte, uint>(ref pThisValue).CompareTo(Unsafe.As<byte, uint>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_I8:
                    return Unsafe.As<byte, long>(ref pThisValue).CompareTo(Unsafe.As<byte, long>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_U8:
                    return Unsafe.As<byte, ulong>(ref pThisValue).CompareTo(Unsafe.As<byte, ulong>(ref pTargetValue));
 
#if RARE_ENUMS
                case CorElementType.ELEMENT_TYPE_R4:
                    return Unsafe.As<byte, float>(ref pThisValue).CompareTo(Unsafe.As<byte, float>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_R8:
                    return Unsafe.As<byte, double>(ref pThisValue).CompareTo(Unsafe.As<byte, double>(ref pTargetValue));
 
                case CorElementType.ELEMENT_TYPE_BOOLEAN:
                    goto case CorElementType.ELEMENT_TYPE_U1;
 
                case CorElementType.ELEMENT_TYPE_CHAR:
                    goto case CorElementType.ELEMENT_TYPE_U2;
 
#if TARGET_32BIT
                case CorElementType.ELEMENT_TYPE_I:
                    goto case CorElementType.ELEMENT_TYPE_I4;
                case CorElementType.ELEMENT_TYPE_U:
                    goto case CorElementType.ELEMENT_TYPE_U4;
#else
                case CorElementType.ELEMENT_TYPE_I:
                    goto case CorElementType.ELEMENT_TYPE_I8;
                case CorElementType.ELEMENT_TYPE_U:
                    goto case CorElementType.ELEMENT_TYPE_U8;
#endif
#endif
 
                default:
                    Debug.Fail("Unknown enum underlying type");
                    return 0;
            }
        }
 
        /// <summary>Converts the value of this instance to its equivalent string representation.</summary>
        /// <remarks>The string representation of the value of this instance.</remarks>
        public override string ToString()
        {
            RuntimeType enumType = (RuntimeType)GetType();
            ref byte rawData = ref this.GetRawData();
            return InternalGetCorElementType() switch
            {
                // Inlined for the most common base types
                CorElementType.ELEMENT_TYPE_I1 => ToString<sbyte, byte>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_U1 => ToStringInlined<byte, byte>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_I2 => ToString<short, ushort>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_U2 => ToString<ushort, ushort>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_I4 => ToStringInlined<int, uint>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_U4 => ToString<uint, uint>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_I8 => ToString<long, ulong>(enumType, ref rawData),
                CorElementType.ELEMENT_TYPE_U8 => ToString<ulong, ulong>(enumType, ref rawData),
                _ => HandleRareTypes(enumType, ref rawData)
            };
 
            [MethodImpl(MethodImplOptions.NoInlining)]
            static string HandleRareTypes(RuntimeType enumType, ref byte rawData) =>
#if RARE_ENUMS
                InternalGetCorElementType(enumType) switch
                {
                    CorElementType.ELEMENT_TYPE_R4 => ToString<float, float>(enumType, ref rawData),
                    CorElementType.ELEMENT_TYPE_R8 => ToString<double, double>(enumType, ref rawData),
                    CorElementType.ELEMENT_TYPE_I => ToString<nint, nuint>(enumType, ref rawData),
                    CorElementType.ELEMENT_TYPE_U => ToString<nuint, nuint>(enumType, ref rawData),
                    CorElementType.ELEMENT_TYPE_CHAR => ToString<char, char>(enumType, ref rawData),
                    _ => throw CreateUnknownEnumTypeException(),
                };
#else
                throw CreateUnknownEnumTypeException();
#endif
        }
 
        /// <summary>Converts the value of this instance to its equivalent string representation using the specified format.</summary>
        /// <param name="format">A format string.</param>
        /// <returns>The string representation of the value of this instance as specified by <paramref name="format"/>.</returns>
        /// <exception cref="FormatException"><paramref name="format"/> contains an invalid specification.</exception>
        /// <exception cref="InvalidOperationException"><paramref name="format"/> equals "X" or "x", but the enumeration type is unknown.</exception>
        public string ToString([StringSyntax(StringSyntaxAttribute.EnumFormat)] string? format)
        {
            if (string.IsNullOrEmpty(format))
            {
                return ToString();
            }
 
            if (format.Length == 1)
            {
                char formatChar = format[0];
                RuntimeType enumType = (RuntimeType)GetType();
                ref byte rawData = ref this.GetRawData();
                return InternalGetCorElementType() switch
                {
                    // Inlined for the most common base types
                    CorElementType.ELEMENT_TYPE_I1 => ToString<sbyte, byte>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_U1 => ToStringInlined<byte, byte>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_I2 => ToString<short, ushort>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_U2 => ToString<ushort, ushort>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_I4 => ToStringInlined<int, uint>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_U4 => ToString<uint, uint>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_I8 => ToString<long, ulong>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_U8 => ToString<ulong, ulong>(enumType, formatChar, ref rawData),
                    _ => HandleRareTypes(enumType, formatChar, ref rawData)
                };
            }
 
            throw CreateInvalidFormatSpecifierException();
 
            [MethodImpl(MethodImplOptions.NoInlining)]
            static string HandleRareTypes(RuntimeType enumType, char formatChar, ref byte rawData) =>
#if RARE_ENUMS
                InternalGetCorElementType(enumType) switch
                {
                    CorElementType.ELEMENT_TYPE_R4 => ToString<float, float>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_R8 => ToString<double, double>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_I => ToString<nint, nuint>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_U => ToString<nuint, nuint>(enumType, formatChar, ref rawData),
                    CorElementType.ELEMENT_TYPE_CHAR => ToString<char, char>(enumType, formatChar, ref rawData),
                    _ => throw CreateUnknownEnumTypeException(),
                };
#else
                throw CreateUnknownEnumTypeException();
#endif
        }
 
        /// <summary>This method overload is obsolete; use <see cref="ToString()"/>.</summary>
        [Obsolete("The provider argument is not used. Use ToString() instead.")]
        public string ToString(IFormatProvider? provider) =>
            ToString();
 
        /// <summary>This method overload is obsolete; use <see cref="ToString(string)"/>.</summary>
        [Obsolete("The provider argument is not used. Use ToString(String) instead.")]
        public string ToString([StringSyntax(StringSyntaxAttribute.EnumFormat)] string? format, IFormatProvider? provider) =>
            ToString(format);
 
        [MethodImpl(MethodImplOptions.NoInlining)] // avoid bloating call sites for underlying types and/or call sites that aren't perf critical
        private static string ToString<TUnderlying, TStorage>(RuntimeType enumType, ref byte rawData)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage> =>
            ToStringInlined<TUnderlying, TStorage>(enumType, ref rawData);
 
        /// <summary>Converts the value of an enum to its equivalent string representation using the default format.</summary>
        /// <typeparam name="TUnderlying">The underlying type for this enum.</typeparam>
        /// <typeparam name="TStorage">The type stored in the EnumInfo.  This will either be the same as <typeparamref name="TUnderlying"/> or its unsigned counterpart if <typeparamref name="TUnderlying"/> is a signed integer.</typeparam>
        /// <param name="enumType">The enum type.</param>
        /// <param name="rawData">A reference to the enum's value.</param>
        /// <returns>The string representation of the value of this instance.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // used for most important types at most important call sites
        private static string ToStringInlined<TUnderlying, TStorage>(RuntimeType enumType, ref byte rawData)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>
        {
            AssertValidGenerics<TUnderlying, TStorage>();
 
            TStorage value = Unsafe.As<byte, TStorage>(ref rawData);
 
            EnumInfo<TStorage> enumInfo = GetEnumInfo<TStorage>(enumType);
            string? result = enumInfo.HasFlagsAttribute ?
                FormatFlagNames(enumInfo, value) :
                GetNameInlined(enumInfo, value);
 
            return result ?? Unsafe.BitCast<TStorage, TUnderlying>(value).ToString()!;
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)] // avoid bloating call sites for underlying types and/or call sites that aren't perf critical
        private static string ToString<TUnderlying, TStorage>(RuntimeType enumType, char format, ref byte rawData)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>, IMinMaxValue<TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>, IMinMaxValue<TStorage> =>
            ToStringInlined<TUnderlying, TStorage>(enumType, format, ref rawData);
 
        /// <summary>Converts the value of an enum to its equivalent string representation using the default format.</summary>
        /// <typeparam name="TUnderlying">The underlying type for this enum.</typeparam>
        /// <typeparam name="TStorage">The type stored in the EnumInfo.  This will either be the same as <typeparamref name="TUnderlying"/> or its unsigned counterpart if <typeparamref name="TUnderlying"/> is a signed integer.</typeparam>
        /// <param name="enumType">The enum type.</param>
        /// <param name="format">A format string.</param>
        /// <param name="rawData">A reference to the enum's value.</param>
        /// <returns>The string representation of the value of this instance as specified by <paramref name="format"/>.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // used for most important types at most important call sites
        private static string ToStringInlined<TUnderlying, TStorage>(RuntimeType enumType, char format, ref byte rawData)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>, IMinMaxValue<TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>, IMinMaxValue<TStorage>
        {
            AssertValidGenerics<TUnderlying, TStorage>();
 
            TStorage value = Unsafe.As<byte, TStorage>(ref rawData);
 
            string? result;
            switch (format | 0x20)
            {
                case 'g':
                    EnumInfo<TStorage> enumInfo = GetEnumInfo<TStorage>(enumType);
                    result = enumInfo.HasFlagsAttribute ? FormatFlagNames(enumInfo, value) : GetNameInlined(enumInfo, value);
                    if (result is null)
                    {
                        goto case 'd';
                    }
                    break;
 
                case 'd':
                    result = Unsafe.BitCast<TStorage, TUnderlying>(value).ToString()!;
                    break;
 
                case 'x':
                    result = FormatNumberAsHex<TStorage>(ref rawData);
                    break;
 
                case 'f':
                    result = FormatFlagNames(GetEnumInfo<TStorage>(enumType), value);
                    if (result is null)
                    {
                        goto case 'd';
                    }
                    break;
 
                default:
                    throw CreateInvalidFormatSpecifierException();
            };
 
            return result;
        }
 
        /// <summary>Formats the data for the underlying value as hex into a new, fixed-length string.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static unsafe string FormatNumberAsHex<TStorage>(ref byte data) where TStorage : struct
        {
            fixed (byte* ptr = &data)
            {
                return string.Create(Unsafe.SizeOf<TStorage>() * 2, (IntPtr)ptr, (destination, intptr) =>
                {
                    bool success = TryFormatNumberAsHex<TStorage>(ref *(byte*)intptr, destination, out int charsWritten);
                    Debug.Assert(success);
                    Debug.Assert(charsWritten == Unsafe.SizeOf<TStorage>() * 2);
                });
            }
        }
 
        /// <summary>Tries to format the data for the underlying value as hex into the destination span.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static bool TryFormatNumberAsHex<TStorage>(ref byte data, Span<char> destination, out int charsWritten) where TStorage : struct
        {
            if (Unsafe.SizeOf<TStorage>() * 2 <= destination.Length)
            {
                if (typeof(TStorage) == typeof(byte) ||
                    typeof(TStorage) == typeof(sbyte))
                {
                    HexConverter.ToCharsBuffer(data, destination);
                }
                else if (typeof(TStorage) == typeof(ushort) ||
                         typeof(TStorage) == typeof(short) ||
                         typeof(TStorage) == typeof(char))
                {
                    ushort value = Unsafe.As<byte, ushort>(ref data);
                    HexConverter.ToCharsBuffer((byte)(value >> 8), destination);
                    HexConverter.ToCharsBuffer((byte)value, destination, 2);
                }
                else if (typeof(TStorage) == typeof(uint) ||
#if TARGET_32BIT
                         typeof(TStorage) == typeof(nuint) ||
                         typeof(TStorage) == typeof(nint) ||
#endif
                         typeof(TStorage) == typeof(int))
                {
                    uint value = Unsafe.As<byte, uint>(ref data);
                    HexConverter.ToCharsBuffer((byte)(value >> 24), destination);
                    HexConverter.ToCharsBuffer((byte)(value >> 16), destination, 2);
                    HexConverter.ToCharsBuffer((byte)(value >> 8), destination, 4);
                    HexConverter.ToCharsBuffer((byte)value, destination, 6);
                }
                else if (typeof(TStorage) == typeof(ulong) ||
#if TARGET_64BIT
                         typeof(TStorage) == typeof(nuint) ||
                         typeof(TStorage) == typeof(nint) ||
#endif
                         typeof(TStorage) == typeof(long))
                {
                    ulong value = Unsafe.As<byte, ulong>(ref data);
                    HexConverter.ToCharsBuffer((byte)(value >> 56), destination);
                    HexConverter.ToCharsBuffer((byte)(value >> 48), destination, 2);
                    HexConverter.ToCharsBuffer((byte)(value >> 40), destination, 4);
                    HexConverter.ToCharsBuffer((byte)(value >> 32), destination, 6);
                    HexConverter.ToCharsBuffer((byte)(value >> 24), destination, 8);
                    HexConverter.ToCharsBuffer((byte)(value >> 16), destination, 10);
                    HexConverter.ToCharsBuffer((byte)(value >> 8), destination, 12);
                    HexConverter.ToCharsBuffer((byte)value, destination, 14);
                }
                else
                {
                    throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
                }
 
                charsWritten = Unsafe.SizeOf<TStorage>() * 2;
                return true;
            }
 
            charsWritten = 0;
            return false;
        }
 
        /// <summary>Converts the specified value of a specified enumerated type to its equivalent string representation according to the specified format.</summary>
        /// <param name="enumType">The enumeration type of the value to convert.</param>
        /// <param name="value">The value to convert.</param>
        /// <param name="format">The output format to use.</param>
        /// <returns>A string representation of <paramref name="value"/>.</returns>
        /// <exception cref="ArgumentNullException">The <paramref name="enumType"/>, <paramref name="value"/>, or <paramref name="format"/> parameter is null.</exception>
        /// <exception cref="ArgumentException">The <paramref name="enumType"/> parameter is not an <see cref="Enum"/> type.</exception>
        /// <exception cref="ArgumentException">The <paramref name="value"/> is from an enumeration that differs in type from <paramref name="enumType"/>.</exception>
        /// <exception cref="ArgumentException">The type of <paramref name="value"/> is not an underlying type of <paramref name="enumType"/>.</exception>
        /// <exception cref="FormatException"><paramref name="format"/> contains an invalid value.</exception>
        /// <exception cref="InvalidOperationException"><paramref name="format"/> equals "X" or "x", but the enumeration type is unknown.</exception>
        public static string Format(Type enumType, object value, [StringSyntax(StringSyntaxAttribute.EnumFormat)] string format)
        {
            ArgumentNullException.ThrowIfNull(value);
            ArgumentNullException.ThrowIfNull(format);
 
            RuntimeType rtType = ValidateRuntimeType(enumType);
 
            Type valueType = value.GetType();
            if (valueType.IsEnum)
            {
                // If the value is an enum type, then it must be equivalent to the specified type.
                if (!valueType.IsEquivalentTo(rtType))
                    throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, valueType, rtType));
 
                // If the format isn't empty, just delegate to ToString(format). The length check is necessary
                // here for compat, as Enum.Format prohibits a null or empty format whereas ToString(string) allows it.
                if (format.Length == 1)
                    return ((Enum)value).ToString(format);
            }
            else
            {
                // The value isn't an enum type. It's either an underlying type or it's invalid,
                // and as an underlying type, it must match the underlying type of the enum type.
                Type underlyingType = GetUnderlyingType(rtType);
                if (valueType != underlyingType)
                    throw new ArgumentException(SR.Format(SR.Arg_EnumFormatUnderlyingTypeAndObjectMustBeSameType, valueType, underlyingType));
 
                // If the format isn't empty, delegate to ToString with the format.
                if (format.Length == 1)
                {
                    char formatChar = format[0];
                    ref byte rawData = ref value.GetRawData();
                    return InternalGetCorElementType(rtType) switch
                    {
                        CorElementType.ELEMENT_TYPE_I1 => ToString<sbyte, byte>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_U1 => ToString<byte, byte>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_I2 => ToString<short, ushort>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_U2 => ToString<ushort, ushort>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_I4 => ToString<int, uint>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_U4 => ToString<uint, uint>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_I8 => ToString<long, ulong>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_U8 => ToString<ulong, ulong>(rtType, formatChar, ref rawData),
#if RARE_ENUMS
                        CorElementType.ELEMENT_TYPE_R4 => ToString<float, float>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_R8 => ToString<double, double>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_I => ToString<nint, nuint>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_U => ToString<nuint, nuint>(rtType, formatChar, ref rawData),
                        CorElementType.ELEMENT_TYPE_CHAR => ToString<char, char>(rtType, formatChar, ref rawData),
#endif
                        _ => throw CreateUnknownEnumTypeException(),
                    };
                }
            }
 
            throw CreateInvalidFormatSpecifierException();
        }
 
        /// <summary>Tries to format the value of the enum into the provided span of characters.</summary>
        /// <param name="destination">The span in which to write this instance's value formatted as a span of characters.</param>
        /// <param name="charsWritten">When this method returns, contains the number of characters that were written in destination.</param>
        /// <param name="format">The format specifier.</param>
        /// <param name="provider">An optional object that supplies culture-specific formatting information for destination. This is ignored.</param>
        /// <returns><see langword="true"/> if the formatting was successful; otherwise, <see langword="false"/>.</returns>
        bool ISpanFormattable.TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
        {
            RuntimeType enumType = (RuntimeType)GetType();
            ref byte rawData = ref this.GetRawData();
            CorElementType corElementType = InternalGetCorElementType();
 
            if (format.IsEmpty)
            {
                return corElementType switch
                {
                    CorElementType.ELEMENT_TYPE_I1 => TryFormatPrimitiveDefault<sbyte, byte>(enumType, (sbyte)rawData, destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_U1 => TryFormatPrimitiveDefault<byte, byte>(enumType, rawData, destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_I2 => TryFormatPrimitiveDefault<short, ushort>(enumType, Unsafe.As<byte, short>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_U2 => TryFormatPrimitiveDefault<ushort, ushort>(enumType, Unsafe.As<byte, ushort>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_I4 => TryFormatPrimitiveDefault<int, uint>(enumType, Unsafe.As<byte, int>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_U4 => TryFormatPrimitiveDefault<uint, uint>(enumType, Unsafe.As<byte, uint>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_I8 => TryFormatPrimitiveDefault<long, ulong>(enumType, Unsafe.As<byte, long>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_U8 => TryFormatPrimitiveDefault<ulong, ulong>(enumType, Unsafe.As<byte, ulong>(ref rawData), destination, out charsWritten),
#if RARE_ENUMS
                    CorElementType.ELEMENT_TYPE_R4 => TryFormatPrimitiveDefault<float, float>(enumType, Unsafe.As<byte, float>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_R8 => TryFormatPrimitiveDefault<double, double>(enumType, Unsafe.As<byte, double>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_I => TryFormatPrimitiveDefault<nint, nuint>(enumType, Unsafe.As<byte, nint>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_U => TryFormatPrimitiveDefault<nuint, nuint>(enumType, Unsafe.As<byte, nuint>(ref rawData), destination, out charsWritten),
                    CorElementType.ELEMENT_TYPE_CHAR => TryFormatPrimitiveDefault<char, char>(enumType, Unsafe.As<byte, char>(ref rawData), destination, out charsWritten),
#endif
                    _ => throw CreateUnknownEnumTypeException(),
                };
            }
            else
            {
                return corElementType switch
                {
                    CorElementType.ELEMENT_TYPE_I1 => TryFormatPrimitiveNonDefault<sbyte, byte>(enumType, (sbyte)rawData, destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_U1 => TryFormatPrimitiveNonDefault<byte, byte>(enumType, rawData, destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_I2 => TryFormatPrimitiveNonDefault<short, ushort>(enumType, Unsafe.As<byte, short>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_U2 => TryFormatPrimitiveNonDefault<ushort, ushort>(enumType, Unsafe.As<byte, ushort>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_I4 => TryFormatPrimitiveNonDefault<int, uint>(enumType, Unsafe.As<byte, int>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_U4 => TryFormatPrimitiveNonDefault<uint, uint>(enumType, Unsafe.As<byte, uint>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_I8 => TryFormatPrimitiveNonDefault<long, ulong>(enumType, Unsafe.As<byte, long>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_U8 => TryFormatPrimitiveNonDefault<ulong, ulong>(enumType, Unsafe.As<byte, ulong>(ref rawData), destination, out charsWritten, format),
#if RARE_ENUMS
                    CorElementType.ELEMENT_TYPE_R4 => TryFormatPrimitiveNonDefault<float, float>(enumType, Unsafe.As<byte, float>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_R8 => TryFormatPrimitiveNonDefault<double, double>(enumType, Unsafe.As<byte, double>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_I => TryFormatPrimitiveNonDefault<nint, nuint>(enumType, Unsafe.As<byte, nint>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_U => TryFormatPrimitiveNonDefault<nuint, nuint>(enumType, Unsafe.As<byte, nuint>(ref rawData), destination, out charsWritten, format),
                    CorElementType.ELEMENT_TYPE_CHAR => TryFormatPrimitiveNonDefault<char, char>(enumType, Unsafe.As<byte, char>(ref rawData), destination, out charsWritten, format),
#endif
                    _ => throw CreateUnknownEnumTypeException(),
                };
            }
        }
 
        /// <summary>Tries to format the value of the enumerated type instance into the provided span of characters.</summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="value"></param>
        /// <param name="destination">The span into which to write the instance's value formatted as a span of characters.</param>
        /// <param name="charsWritten">When this method returns, contains the number of characters that were written in <paramref name="destination"/>.</param>
        /// <param name="format">A span containing the character that represents the standard format string that defines the acceptable format of destination. This may be empty, or "g", "d", "f", or "x".</param>
        /// <returns><see langword="true"/> if the formatting was successful; otherwise, <see langword="false"/> if the destination span wasn't large enough to contain the formatted value.</returns>
        /// <exception cref="FormatException">The format parameter contains an invalid value.</exception>
        public static unsafe bool TryFormat<TEnum>(TEnum value, Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.EnumFormat)] ReadOnlySpan<char> format = default) where TEnum : struct
        {
            RuntimeType rt = (RuntimeType)typeof(TEnum);
            Type underlyingType = typeof(TEnum).GetEnumUnderlyingType();
 
            // If the format is empty, which is the most common case, delegate to the default implementation that doesn't take a format.
            // That implementation is more optimized. Doing this check here means in the common case where TryFormat is inlined and no format
            // is provided, this whole call can become just a call to the default method.  Even if it's not inlined, this check would still otherwise
            // be necessary for semantics inside of TryFormatPrimitiveNonDefault, so we can just do it here instead.
            if (format.IsEmpty)
            {
                if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault<int, uint>(rt, *(int*)&value, destination, out charsWritten);
                if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault<uint, uint>(rt, *(uint*)&value, destination, out charsWritten);
                if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault<long, ulong>(rt, *(long*)&value, destination, out charsWritten);
                if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault<ulong, ulong>(rt, *(ulong*)&value, destination, out charsWritten);
                if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault<byte, byte>(rt, *(byte*)&value, destination, out charsWritten);
                if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault<sbyte, byte>(rt, *(sbyte*)&value, destination, out charsWritten);
                if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault<short, ushort>(rt, *(short*)&value, destination, out charsWritten);
                if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault<ushort, ushort>(rt, *(ushort*)&value, destination, out charsWritten);
#if RARE_ENUMS
                if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault<nint, nuint>(rt, *(nint*)&value, destination, out charsWritten);
                if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault<nuint, nuint>(rt, *(nuint*)&value, destination, out charsWritten);
                if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault<float, float>(rt, *(float*)&value, destination, out charsWritten);
                if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault<double, double>(rt, *(double*)&value, destination, out charsWritten);
                if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault<char, char>(rt, *(char*)&value, destination, out charsWritten);
#endif
            }
            else
            {
                if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault<int, uint>(rt, *(int*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault<uint, uint>(rt, *(uint*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault<long, ulong>(rt, *(long*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault<ulong, ulong>(rt, *(ulong*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault<byte, byte>(rt, *(byte*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault<sbyte, byte>(rt, *(sbyte*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault<short, ushort>(rt, *(short*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault<ushort, ushort>(rt, *(ushort*)&value, destination, out charsWritten, format);
#if RARE_ENUMS
                if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault<nint, nuint>(rt, *(nint*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault<nuint, nuint>(rt, *(nuint*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault<float, float>(rt, *(float*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault<double, double>(rt, *(double*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault<char, char>(rt, *(char*)&value, destination, out charsWritten, format);
#endif
            }
 
            throw CreateUnknownEnumTypeException();
        }
 
        /// <summary>Tries to format the value of the enumerated type instance into the provided span of characters.</summary>
        /// <remarks>
        /// This is same as the implementation for <see cref="TryFormat"/>. It is separated out as <see cref="TryFormat"/> has constrains on the TEnum,
        /// and we internally want to use this method in cases where we dynamically validate a generic T is an enum rather than T implementing
        /// those constraints. It's a manual copy/paste right now to avoid pressure on the JIT's inlining mechanisms.
        /// </remarks>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // format is most frequently a constant, and we want it exposed to the implementation; this should be inlined automatically, anyway
        internal static unsafe bool TryFormatUnconstrained<TEnum>(TEnum value, Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.EnumFormat)] ReadOnlySpan<char> format = default)
        {
            Debug.Assert(typeof(TEnum).IsEnum);
            Debug.Assert(value is not null);
 
            RuntimeType rt = (RuntimeType)typeof(TEnum);
            Type underlyingType = typeof(TEnum).GetEnumUnderlyingType();
 
            // If the format is empty, which is the most common case, delegate to the default implementation that doesn't take a format.
            // That implementation is more optimized. Doing this check here means in the common case where TryFormat is inlined and no format
            // is provided, this whole call can become just a call to the default method.  Even if it's not inlined, this check would still otherwise
            // be necessary for semantics inside of TryFormatPrimitiveNonDefault, so we can just do it here instead.
            if (format.IsEmpty)
            {
                if (underlyingType == typeof(int)) return TryFormatPrimitiveDefault<int, uint>(rt, *(int*)&value, destination, out charsWritten);
                if (underlyingType == typeof(uint)) return TryFormatPrimitiveDefault<uint, uint>(rt, *(uint*)&value, destination, out charsWritten);
                if (underlyingType == typeof(long)) return TryFormatPrimitiveDefault<long, ulong>(rt, *(long*)&value, destination, out charsWritten);
                if (underlyingType == typeof(ulong)) return TryFormatPrimitiveDefault<ulong, ulong>(rt, *(ulong*)&value, destination, out charsWritten);
                if (underlyingType == typeof(byte)) return TryFormatPrimitiveDefault<byte, byte>(rt, *(byte*)&value, destination, out charsWritten);
                if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveDefault<sbyte, byte>(rt, *(sbyte*)&value, destination, out charsWritten);
                if (underlyingType == typeof(short)) return TryFormatPrimitiveDefault<short, ushort>(rt, *(short*)&value, destination, out charsWritten);
                if (underlyingType == typeof(ushort)) return TryFormatPrimitiveDefault<ushort, ushort>(rt, *(ushort*)&value, destination, out charsWritten);
#if RARE_ENUMS
                if (underlyingType == typeof(nint)) return TryFormatPrimitiveDefault<nint, nuint>(rt, *(nint*)&value, destination, out charsWritten);
                if (underlyingType == typeof(nuint)) return TryFormatPrimitiveDefault<nuint, nuint>(rt, *(nuint*)&value, destination, out charsWritten);
                if (underlyingType == typeof(float)) return TryFormatPrimitiveDefault<float, float>(rt, *(float*)&value, destination, out charsWritten);
                if (underlyingType == typeof(double)) return TryFormatPrimitiveDefault<double, double>(rt, *(double*)&value, destination, out charsWritten);
                if (underlyingType == typeof(char)) return TryFormatPrimitiveDefault<char, char>(rt, *(char*)&value, destination, out charsWritten);
#endif
            }
            else
            {
                if (underlyingType == typeof(int)) return TryFormatPrimitiveNonDefault<int, uint>(rt, *(int*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(uint)) return TryFormatPrimitiveNonDefault<uint, uint>(rt, *(uint*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(long)) return TryFormatPrimitiveNonDefault<long, ulong>(rt, *(long*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(ulong)) return TryFormatPrimitiveNonDefault<ulong, ulong>(rt, *(ulong*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(byte)) return TryFormatPrimitiveNonDefault<byte, byte>(rt, *(byte*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(sbyte)) return TryFormatPrimitiveNonDefault<sbyte, byte>(rt, *(sbyte*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(short)) return TryFormatPrimitiveNonDefault<short, ushort>(rt, *(short*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(ushort)) return TryFormatPrimitiveNonDefault<ushort, ushort>(rt, *(ushort*)&value, destination, out charsWritten, format);
#if RARE_ENUMS
                if (underlyingType == typeof(nint)) return TryFormatPrimitiveNonDefault<nint, nuint>(rt, *(nint*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(nuint)) return TryFormatPrimitiveNonDefault<nuint, nuint>(rt, *(nuint*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(float)) return TryFormatPrimitiveNonDefault<float, float>(rt, *(float*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(double)) return TryFormatPrimitiveNonDefault<double, double>(rt, *(double*)&value, destination, out charsWritten, format);
                if (underlyingType == typeof(char)) return TryFormatPrimitiveNonDefault<char, char>(rt, *(char*)&value, destination, out charsWritten, format);
#endif
            }
 
            throw CreateUnknownEnumTypeException();
        }
 
        /// <summary>Core implementation for  <see cref="TryFormat"/> when no format specifier was provided.</summary>
        private static bool TryFormatPrimitiveDefault<TUnderlying, TStorage>(RuntimeType enumType, TUnderlying value, Span<char> destination, out int charsWritten)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>, IMinMaxValue<TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>, IMinMaxValue<TStorage>
        {
            AssertValidGenerics<TUnderlying, TStorage>();
 
            EnumInfo<TStorage> enumInfo = GetEnumInfo<TStorage>(enumType);
 
            if (!enumInfo.HasFlagsAttribute)
            {
                if (GetNameInlined(enumInfo, Unsafe.BitCast<TUnderlying, TStorage>(value)) is string enumName)
                {
                    if (enumName.TryCopyTo(destination))
                    {
                        charsWritten = enumName.Length;
                        return true;
                    }
 
                    charsWritten = 0;
                    return false;
                }
            }
            else
            {
                bool destinationIsTooSmall = false;
                if (TryFormatFlagNames(enumInfo, Unsafe.BitCast<TUnderlying, TStorage>(value), destination, out charsWritten, ref destinationIsTooSmall) || destinationIsTooSmall)
                {
                    return !destinationIsTooSmall;
                }
            }
 
            return value.TryFormat(destination, out charsWritten, format: default, provider: null);
        }
 
        /// <summary>Core implementation for  <see cref="TryFormat"/> when a format specifier was provided.</summary>
        private static bool TryFormatPrimitiveNonDefault<TUnderlying, TStorage>(RuntimeType enumType, TUnderlying value, Span<char> destination, out int charsWritten, ReadOnlySpan<char> format)
            where TUnderlying : struct, INumber<TUnderlying>, IBitwiseOperators<TUnderlying, TUnderlying, TUnderlying>, IMinMaxValue<TUnderlying>
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>, IMinMaxValue<TStorage>
        {
            AssertValidGenerics<TUnderlying, TStorage>();
 
            Debug.Assert(!format.IsEmpty);
 
            if (format.Length == 1)
            {
                switch (format[0] | 0x20)
                {
                    case 'g':
                        return TryFormatPrimitiveDefault<TUnderlying, TStorage>(enumType, value, destination, out charsWritten);
 
                    case 'd':
                        return value.TryFormat(destination, out charsWritten, format: default, provider: null);
 
                    case 'x':
                        return TryFormatNumberAsHex<TStorage>(ref Unsafe.As<TUnderlying, byte>(ref value), destination, out charsWritten);
 
                    case 'f':
                        bool destinationIsTooSmall = false;
                        if (TryFormatFlagNames(GetEnumInfo<TStorage>(enumType), Unsafe.BitCast<TUnderlying, TStorage>(value), destination, out charsWritten, ref destinationIsTooSmall) ||
                            destinationIsTooSmall)
                        {
                            return !destinationIsTooSmall;
                        }
                        goto case 'd';
                }
            }
 
            throw CreateInvalidFormatSpecifierException();
        }
 
        /// <summary>Tries to create a string representation of an enum as either a single constant name or multiple delimited constant names.</summary>
        /// <returns>The formatted string if the value could be fully represented by enum constants, or else null.</returns>
        private static string? FormatFlagNames<TStorage>(EnumInfo<TStorage> enumInfo, TStorage resultValue)
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>
        {
            string[] names = enumInfo.Names;
            TStorage[] values = enumInfo.Values;
            Debug.Assert(names.Length == values.Length);
 
            string? result = GetSingleFlagsEnumNameForValue(resultValue, names, values, out int index);
            if (result is null)
            {
                // With a ulong result value, regardless of the enum's base type, the maximum
                // possible number of consistent name/values we could have is 64, since every
                // value is made up of one or more bits, and when we see values and incorporate
                // their names, we effectively switch off those bits.
                Span<int> foundItems = stackalloc int[64];
                if (TryFindFlagsNames(resultValue, names, values, index, foundItems, out int resultLength, out int foundItemsCount))
                {
                    foundItems = foundItems.Slice(0, foundItemsCount);
                    int length = GetMultipleEnumsFlagsFormatResultLength(resultLength, foundItemsCount);
 
                    result = string.FastAllocateString(length);
                    WriteMultipleFoundFlagsNames(names, foundItems, new Span<char>(ref result.GetRawStringData(), result.Length));
                }
            }
 
            return result;
        }
 
        /// <summary>Tries to format into a span a representation of an enum as either a single constant name or multiple delimited constant names.</summary>
        /// <returns>
        /// true if the value could be fully represented by enum constants and if the formatted value could fit into the destination span; otherwise, false.
        /// If false, <paramref name="isDestinationTooSmall"/> is used to disambiguate the reason for the failure.
        /// </returns>
        private static bool TryFormatFlagNames<TStorage>(EnumInfo<TStorage> enumInfo, TStorage resultValue, Span<char> destination, out int charsWritten, ref bool isDestinationTooSmall)
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>
        {
            Debug.Assert(!isDestinationTooSmall);
 
            string[] names = enumInfo.Names;
            TStorage[] values = enumInfo.Values;
            Debug.Assert(names.Length == values.Length);
 
            if (GetSingleFlagsEnumNameForValue(resultValue, names, values, out int index) is string singleEnumFlagsFormat)
            {
                if (singleEnumFlagsFormat.TryCopyTo(destination))
                {
                    charsWritten = singleEnumFlagsFormat.Length;
                    return true;
                }
 
                isDestinationTooSmall = true;
            }
            else
            {
                // With a ulong result value, regardless of the enum's base type, the maximum
                // possible number of consistent name/values we could have is 64, since every
                // value is made up of one or more bits, and when we see values and incorporate
                // their names, we effectively switch off those bits.
                Span<int> foundItems = stackalloc int[64];
                if (TryFindFlagsNames(resultValue, names, values, index, foundItems, out int resultLength, out int foundItemsCount))
                {
                    foundItems = foundItems.Slice(0, foundItemsCount);
                    int length = GetMultipleEnumsFlagsFormatResultLength(resultLength, foundItemsCount);
 
                    if (length <= destination.Length)
                    {
                        charsWritten = length;
                        WriteMultipleFoundFlagsNames(names, foundItems, destination);
                        return true;
                    }
 
                    isDestinationTooSmall = true;
                }
            }
 
            charsWritten = 0;
            return false;
        }
 
        /// <summary>
        /// Calculates how many characters will be in a formatted value, where there are <paramref name="foundItemsCount"/>
        /// names whose lengths all sum to <paramref name="resultLength"/>.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // used twice, once from string-based and once from span-based code path
        private static int GetMultipleEnumsFlagsFormatResultLength(int resultLength, int foundItemsCount)
        {
            Debug.Assert(foundItemsCount >= 2 && foundItemsCount <= 64, $"{nameof(foundItemsCount)} == {foundItemsCount}");
 
            const int SeparatorStringLength = 2; // ", "
            int allSeparatorsLength = SeparatorStringLength * (foundItemsCount - 1); // this can't overflow
            return checked(resultLength + allSeparatorsLength);
        }
 
        /// <summary>Tries to find the single named constant for the specified value, or else the index where we left off searching after not finding it.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // used twice, once from string-based and once from span-based code path
        private static string? GetSingleFlagsEnumNameForValue<TStorage>(TStorage resultValue, string[] names, TStorage[] values, out int index)
            where TStorage : struct, INumber<TStorage>
        {
            // Values are sorted, so if the incoming value is 0, we can check to see whether
            // the first entry matches it, in which case we can return its name; otherwise,
            // we can just return "0".
            if (resultValue == TStorage.Zero)
            {
                index = 0;
                return values.Length > 0 && values[0] == TStorage.Zero ?
                    names[0] :
                    "0";
            }
 
            // Walk from largest to smallest. It's common to have a flags enum with a single
            // value that matches a single entry, in which case we can just return the existing
            // name string.
            int i;
            for (i = values.Length - 1; (uint)i < (uint)values.Length; i--)
            {
                if (values[i] <= resultValue)
                {
                    if (values[i] == resultValue)
                    {
                        index = i;
                        return names[i];
                    }
 
                    break;
                }
            }
 
            index = i;
            return null;
        }
 
        /// <summary>Tries to compute the indices of all named constants that or together to equal the specified value.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // used twice, once from string-based and once from span-based code path
        private static bool TryFindFlagsNames<TStorage>(TStorage resultValue, string[] names, TStorage[] values, int index, Span<int> foundItems, out int resultLength, out int foundItemsCount)
            where TStorage : struct, INumber<TStorage>, IBitwiseOperators<TStorage, TStorage, TStorage>
        {
            // Now look for multiple matches, storing the indices of the values
            // into our span.
            resultLength = 0;
            foundItemsCount = 0;
 
            while (true)
            {
                if ((uint)index >= (uint)values.Length)
                {
                    break;
                }
 
                TStorage currentValue = values[index];
                if (index == 0 && currentValue == TStorage.Zero)
                {
                    break;
                }
 
                if ((resultValue & currentValue) == currentValue)
                {
                    resultValue &= ~currentValue;
                    foundItems[foundItemsCount] = index;
                    foundItemsCount++;
                    resultLength = checked(resultLength + names[index].Length);
                    if (resultValue == TStorage.Zero)
                    {
                        break;
                    }
                }
 
                index--;
            }
 
            // If we exhausted looking through all the values and we still have
            // a non-zero result, we couldn't match the result to only named values.
            // In that case, we return null and let the call site just generate
            // a string for the integral value if it desires.
            return resultValue == TStorage.Zero;
        }
 
        /// <summary>Concatenates the names of the found items into the destination span.</summary>
        /// <remarks>The destination must have already been verified long enough to store the resulting data.</remarks>
        [MethodImpl(MethodImplOptions.AggressiveInlining)] // used twice, once from string-based and once from span-based code path
        private static void WriteMultipleFoundFlagsNames(string[] names, ReadOnlySpan<int> foundItems, Span<char> destination)
        {
            Debug.Assert(foundItems.Length >= 2, $"{nameof(foundItems)} == {foundItems.Length}");
 
            for (int i = foundItems.Length - 1; i != 0; i--)
            {
                string name = names[foundItems[i]];
                name.CopyTo(destination);
                destination = destination.Slice(name.Length);
                Span<char> afterSeparator = destination.Slice(2); // done before copying ", " to eliminate those two bounds checks
                destination[0] = EnumSeparatorChar;
                destination[1] = ' ';
                destination = afterSeparator;
            }
 
            names[foundItems[0]].CopyTo(destination);
        }
 
        private static RuntimeType ValidateRuntimeType(Type enumType)
        {
            ArgumentNullException.ThrowIfNull(enumType);
 
            RuntimeType? rt = enumType as RuntimeType;
            if (rt is null || !rt.IsActualEnum)
            {
                ThrowInvalidRuntimeType(enumType);
            }
 
#if NATIVEAOT
            // Check for the unfortunate "typeof(Outer<>.InnerEnum)" corner case.
            // https://github.com/dotnet/runtime/issues/7976
            if (rt.ContainsGenericParameters)
                throw new InvalidOperationException(SR.Format(SR.Arg_OpenType, rt.ToString()));
#endif
 
            return rt;
        }
 
        [DoesNotReturn]
        private static void ThrowInvalidRuntimeType(Type enumType) =>
            throw new ArgumentException(enumType is not RuntimeType ? SR.Arg_MustBeType : SR.Arg_MustBeEnum, nameof(enumType));
 
        private static void ThrowInvalidEmptyParseArgument() =>
            throw new ArgumentException(SR.Arg_MustContainEnumInfo, "value");
 
        [MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/78300
        private static FormatException CreateInvalidFormatSpecifierException() =>
            new FormatException(SR.Format_InvalidEnumFormatSpecification);
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static InvalidOperationException CreateUnknownEnumTypeException() =>
            new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
 
        public TypeCode GetTypeCode() =>
            InternalGetCorElementType() switch
            {
                CorElementType.ELEMENT_TYPE_I1 => TypeCode.SByte,
                CorElementType.ELEMENT_TYPE_U1 => TypeCode.Byte,
                CorElementType.ELEMENT_TYPE_I2 => TypeCode.Int16,
                CorElementType.ELEMENT_TYPE_U2 => TypeCode.UInt16,
                CorElementType.ELEMENT_TYPE_I4 => TypeCode.Int32,
                CorElementType.ELEMENT_TYPE_U4 => TypeCode.UInt32,
                CorElementType.ELEMENT_TYPE_I8 => TypeCode.Int64,
                CorElementType.ELEMENT_TYPE_U8 => TypeCode.UInt64,
                CorElementType.ELEMENT_TYPE_CHAR => TypeCode.Char,
                // There's no TypeCode for nint or nuint, and our VB support (or at least
                // tests) needs to be updated in order to include float/double here.
                _ => throw CreateUnknownEnumTypeException(),
            };
 
        bool IConvertible.ToBoolean(IFormatProvider? provider) => Convert.ToBoolean(GetValue());
        char IConvertible.ToChar(IFormatProvider? provider) => Convert.ToChar(GetValue());
        sbyte IConvertible.ToSByte(IFormatProvider? provider) => Convert.ToSByte(GetValue());
        byte IConvertible.ToByte(IFormatProvider? provider) => Convert.ToByte(GetValue());
        short IConvertible.ToInt16(IFormatProvider? provider) => Convert.ToInt16(GetValue());
        ushort IConvertible.ToUInt16(IFormatProvider? provider) => Convert.ToUInt16(GetValue());
        int IConvertible.ToInt32(IFormatProvider? provider) => Convert.ToInt32(GetValue());
        uint IConvertible.ToUInt32(IFormatProvider? provider) => Convert.ToUInt32(GetValue());
        long IConvertible.ToInt64(IFormatProvider? provider) => Convert.ToInt64(GetValue());
        ulong IConvertible.ToUInt64(IFormatProvider? provider) => Convert.ToUInt64(GetValue());
        float IConvertible.ToSingle(IFormatProvider? provider) => Convert.ToSingle(GetValue());
        double IConvertible.ToDouble(IFormatProvider? provider) => Convert.ToDouble(GetValue());
        decimal IConvertible.ToDecimal(IFormatProvider? provider) => Convert.ToDecimal(GetValue());
        DateTime IConvertible.ToDateTime(IFormatProvider? provider) => throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Enum", "DateTime"));
        object IConvertible.ToType(Type type, IFormatProvider? provider) => Convert.DefaultToType(this, type, provider);
 
        public static object ToObject(Type enumType, object value)
        {
            ArgumentNullException.ThrowIfNull(value);
 
            switch (Convert.GetTypeCode(value))
            {
                case TypeCode.Int32: return ToObject(enumType, (int)value);
                case TypeCode.SByte: return ToObject(enumType, (sbyte)value);
                case TypeCode.Int16: return ToObject(enumType, (short)value);
                case TypeCode.Int64: return ToObject(enumType, (long)value);
                case TypeCode.UInt32: return ToObject(enumType, (uint)value);
                case TypeCode.Byte: return ToObject(enumType, (byte)value);
                case TypeCode.UInt16: return ToObject(enumType, (ushort)value);
                case TypeCode.UInt64: return ToObject(enumType, (ulong)value);
                case TypeCode.Single: return ToObject(enumType, BitConverter.SingleToInt32Bits((float)value));
                case TypeCode.Double: return ToObject(enumType, BitConverter.DoubleToInt64Bits((double)value));
                case TypeCode.Char: return ToObject(enumType, (char)value);
                case TypeCode.Boolean: return ToObject(enumType, (bool)value ? 1L : 0L);
            };
 
            Type valueType = value.GetType();
            if (valueType.IsEnum)
            {
                valueType = valueType.GetEnumUnderlyingType();
            }
 
            if (valueType == typeof(nint)) ToObject(enumType, (nint)value);
            if (valueType == typeof(nuint)) ToObject(enumType, (nuint)value);
 
            throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value));
        }
 
        [CLSCompliant(false)]
        public static object ToObject(Type enumType, sbyte value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        public static object ToObject(Type enumType, short value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        public static object ToObject(Type enumType, int value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        public static object ToObject(Type enumType, byte value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        [CLSCompliant(false)]
        public static object ToObject(Type enumType, ushort value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        [CLSCompliant(false)]
        public static object ToObject(Type enumType, uint value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        public static object ToObject(Type enumType, long value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, value);
 
        [CLSCompliant(false)]
        public static object ToObject(Type enumType, ulong value) =>
            InternalBoxEnum(ValidateRuntimeType(enumType).TypeHandle, unchecked((long)value));
 
        private static object InternalBoxEnum(RuntimeTypeHandle type, long value)
        {
            ReadOnlySpan<byte> rawData = MemoryMarshal.AsBytes(new ReadOnlySpan<long>(ref value));
            // On little-endian systems, we can always use the pointer to the start of the scratch space
            // as memory layout since the least-significant bit is at the lowest address.
            // For big-endian systems, the least-significant bit is at the highest address, so we need to adjust
            // our starting ref to the correct offset from the end of the scratch space to get the value to box.
            if (!BitConverter.IsLittleEndian)
            {
                int size = RuntimeHelpers.SizeOf(type);
                rawData = rawData.Slice(sizeof(long) - size);
            }
 
            return RuntimeHelpers.Box(ref MemoryMarshal.GetReference(rawData), type)!;
        }
 
        internal static bool AreSequentialFromZero<TStorage>(TStorage[] values) where TStorage : struct, INumber<TStorage>
        {
            for (int i = 0; i < values.Length; i++)
            {
                if (ulong.CreateTruncating(values[i]) != (ulong)i)
                {
                    return false;
                }
            }
 
            return true;
        }
 
        internal static bool AreSorted<TStorage>(TStorage[] values) where TStorage : struct, IComparable<TStorage>
        {
            for (int i = 1; i < values.Length; i++)
            {
                if (values[i - 1].CompareTo(values[i]) > 0)
                {
                    return false;
                }
            }
 
            return true;
        }
 
        [Conditional("DEBUG")]
        private static void AssertValidGenerics<TUnderlying, TStorage>()
        {
            Debug.Assert(
                typeof(TUnderlying) == typeof(sbyte) ||
                typeof(TUnderlying) == typeof(byte) ||
                typeof(TUnderlying) == typeof(short) ||
                typeof(TUnderlying) == typeof(ushort) ||
                typeof(TUnderlying) == typeof(int) ||
                typeof(TUnderlying) == typeof(uint) ||
                typeof(TUnderlying) == typeof(long) ||
                typeof(TUnderlying) == typeof(ulong) ||
                typeof(TUnderlying) == typeof(nint) ||
                typeof(TUnderlying) == typeof(nuint) ||
                typeof(TUnderlying) == typeof(float) ||
                typeof(TUnderlying) == typeof(double) ||
                typeof(TUnderlying) == typeof(char));
 
            if (typeof(TUnderlying) == typeof(sbyte)) Debug.Assert(typeof(TStorage) == typeof(byte));
            else if (typeof(TUnderlying) == typeof(short)) Debug.Assert(typeof(TStorage) == typeof(ushort));
            else if (typeof(TUnderlying) == typeof(int)) Debug.Assert(typeof(TStorage) == typeof(uint));
            else if (typeof(TUnderlying) == typeof(long)) Debug.Assert(typeof(TStorage) == typeof(ulong));
            else if (typeof(TUnderlying) == typeof(nint)) Debug.Assert(typeof(TStorage) == typeof(nuint));
            else Debug.Assert(typeof(TUnderlying) == typeof(TStorage));
        }
    }
}