Implement new API GetEnumValuesAsUnderlyingType (#73057)
authorLakshan Fernando <lakshanf@microsoft.com>
Tue, 2 Aug 2022 23:24:16 +0000 (16:24 -0700)
committerGitHub <noreply@github.com>
Tue, 2 Aug 2022 23:24:16 +0000 (16:24 -0700)
* Implement new API GetEnumValuesAsUnderlyingType

* Update src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* FB

* Update src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* FB2

* Apply suggestions from code review

* Update src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.cs

* Update src/libraries/System.Reflection/tests/TypeInfoTests.cs

* Update src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs

* Update src/libraries/System.Private.CoreLib/src/System/Type.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* Update src/libraries/System.Private.CoreLib/src/System/Enum.cs

Co-authored-by: Stephen Toub <stoub@microsoft.com>
* FB3

* Update src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* Update src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* Comment FB

* workaround mono test failures

* Update src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
* remove ifdefd

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
13 files changed:
src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeType.cs
src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/EnumConverter.cs
src/libraries/System.Private.CoreLib/src/System/Enum.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/SignatureType.cs
src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
src/libraries/System.Private.CoreLib/src/System/Type.cs
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Types/RoType.cs
src/libraries/System.Reflection.MetadataLoadContext/tests/src/SampleMetadata/SampleMetadata.cs
src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeTests.cs
src/libraries/System.Reflection/tests/TypeInfoTests.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Runtime/tests/System/EnumTests.cs
src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs

index cce84ae..719424c 100644 (file)
@@ -111,6 +111,14 @@ namespace System
             return result;
         }
 
+        public sealed override Array GetEnumValuesAsUnderlyingType()
+        {
+            if (!IsActualEnum)
+                throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+            return (Array)Enum.GetEnumInfo(this).ValuesAsUnderlyingType.Clone();
+        }
+
         internal bool IsActualEnum
             => TryGetEEType(out EETypePtr eeType) && eeType.IsEnum;
     }
index 81e10e5..5dcc263 100644 (file)
@@ -62,7 +62,7 @@ namespace System.ComponentModel
         /// </summary>
         protected virtual IComparer Comparer => InvariantComparer.Default;
 
-        private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, CultureInfo? culture)
+        private static long GetEnumValue(bool isUnderlyingTypeUInt64, object enumVal, CultureInfo? culture)
         {
             return isUnderlyingTypeUInt64 ?
                 unchecked((long)Convert.ToUInt64(enumVal, culture)) :
@@ -85,7 +85,7 @@ namespace System.ComponentModel
                         string[] values = strValue.Split(',');
                         foreach (string v in values)
                         {
-                            convertedValue |= GetEnumValue(isUnderlyingTypeUInt64, (Enum)Enum.Parse(EnumType, v, true), culture);
+                            convertedValue |= GetEnumValue(isUnderlyingTypeUInt64, Enum.Parse(EnumType, v, true), culture);
                         }
                         return Enum.ToObject(EnumType, convertedValue);
                     }
@@ -171,14 +171,14 @@ namespace System.ComponentModel
                     bool isUnderlyingTypeUInt64 = Enum.GetUnderlyingType(EnumType) == typeof(ulong);
                     List<Enum> flagValues = new List<Enum>();
 
-                    Array objValues = Enum.GetValues(EnumType);
+                    Array objValues = Enum.GetValuesAsUnderlyingType(EnumType);
                     long[] ulValues = new long[objValues.Length];
                     for (int idx = 0; idx < objValues.Length; idx++)
                     {
-                        ulValues[idx] = GetEnumValue(isUnderlyingTypeUInt64, (Enum)objValues.GetValue(idx)!, culture);
+                        ulValues[idx] = GetEnumValue(isUnderlyingTypeUInt64, objValues.GetValue(idx)!, culture);
                     }
 
-                    long longValue = GetEnumValue(isUnderlyingTypeUInt64, (Enum)value, culture);
+                    long longValue = GetEnumValue(isUnderlyingTypeUInt64, value, culture);
                     bool valueFound = true;
                     while (valueFound)
                     {
index e3f9941..8940441 100644 (file)
@@ -315,13 +315,46 @@ namespace System
             (TEnum[])GetValues(typeof(TEnum));
 #endif
 
-        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload instead.")]
+        [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>
+        /// This method can be used to get enumeration values when creating an array of the enumeration type is challenging.
+        /// For example, <see cref="T:System.Reflection.MetadataLoadContext" /> or on a platform where runtime codegen is not available.
+        /// </remarks>
+        /// <returns>An array that contains the values of the underlying type constants in enumType.</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>
+        /// This method can be used to get enumeration values when creating an array of the enumeration type is challenging.
+        /// For example, <see cref="T:System.Reflection.MetadataLoadContext" /> or on a platform where runtime codegen is not available.
+        /// </remarks>
+        /// <returns>An array that contains the values of the underlying type constants in  <paramref name="enumType" />.</returns>
+        /// <exception cref="ArgumentNullException">
+        /// Thrown when the enumeration type is null.
+        /// </exception>
+        /// <exception cref="ArgumentException">
+        /// Thrown when the type is not an enumeration type.
+        /// </exception>
+        public static Array GetValuesAsUnderlyingType(Type enumType)
+        {
+            ArgumentNullException.ThrowIfNull(enumType);
+            return enumType.GetEnumValuesAsUnderlyingType();
+        }
+
         [Intrinsic]
         public bool HasFlag(Enum flag)
         {
index 1d7251e..56d9d45 100644 (file)
@@ -99,7 +99,7 @@ namespace System.Reflection
         public sealed override string GetEnumName(object value) => throw new NotSupportedException(SR.NotSupported_SignatureType);
         public sealed override string[] GetEnumNames() => throw new NotSupportedException(SR.NotSupported_SignatureType);
         public sealed override Type GetEnumUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SignatureType);
-        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues<TEnum> instead.")]
+        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
         public sealed override Array GetEnumValues() => throw new NotSupportedException(SR.NotSupported_SignatureType);
         public sealed override Guid GUID => throw new NotSupportedException(SR.NotSupported_SignatureType);
         protected sealed override TypeCode GetTypeCodeImpl() => throw new NotSupportedException(SR.NotSupported_SignatureType);
index 0182163..81d6f99 100644 (file)
@@ -118,7 +118,7 @@ namespace System
             return new ReadOnlySpan<string>(ret).ToArray();
         }
 
-        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload instead.")]
+        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
         public override Array GetEnumValues()
         {
             if (!IsActualEnum)
@@ -139,6 +139,107 @@ namespace System
             return ret;
         }
 
+        /// <summary>
+        /// Retrieves an array of the values of the underlying type constants in a specified enumeration type.
+        /// </summary>
+        /// <remarks>
+        /// This method can be used to get enumeration values when creating an array of the enumeration type is challenging.
+        /// For example, <see cref="T:System.Reflection.MetadataLoadContext" /> or on a platform where runtime codegen is not available.
+        /// </remarks>
+        /// <returns>An array that contains the values of the underlying type constants in enumType.</returns>
+        /// <exception cref="ArgumentException">
+        /// Thrown when the type is not Enum
+        /// </exception>
+        public override Array GetEnumValuesAsUnderlyingType()
+        {
+            if (!IsActualEnum)
+                throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+            // Get all of the values
+            ulong[] values = Enum.InternalGetValues(this);
+
+            switch (RuntimeTypeHandle.GetCorElementType(Enum.InternalGetUnderlyingType(this)))
+            {
+
+                case CorElementType.ELEMENT_TYPE_U1:
+                    {
+                        var ret = new byte[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (byte)values[i];
+                        }
+                        return ret;
+                    }
+
+                case CorElementType.ELEMENT_TYPE_U2:
+                    {
+                        var ret = new ushort[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (ushort)values[i];
+                        }
+                        return ret;
+                    }
+
+                case CorElementType.ELEMENT_TYPE_U4:
+                    {
+                        var ret = new uint[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (uint)values[i];
+                        }
+                        return ret;
+                    }
+
+                case CorElementType.ELEMENT_TYPE_U8:
+                    {
+                        return (Array)values.Clone();
+                    }
+
+                case CorElementType.ELEMENT_TYPE_I1:
+                    {
+                        var ret = new sbyte[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (sbyte)values[i];
+                        }
+                        return ret;
+                    }
+
+                case CorElementType.ELEMENT_TYPE_I2:
+                    {
+                        var ret = new short[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (short)values[i];
+                        }
+                        return ret;
+                    }
+
+                case CorElementType.ELEMENT_TYPE_I4:
+                    {
+                        var ret = new int[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (int)values[i];
+                        }
+                        return ret;
+                    }
+
+                case CorElementType.ELEMENT_TYPE_I8:
+                    {
+                        var ret = new long[values.Length];
+                        for (int i = 0; i < values.Length; i++)
+                        {
+                            ret[i] = (long)values[i];
+                        }
+                        return ret;
+                    }
+                default:
+                    throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
+            }
+        }
+
         public override Type GetEnumUnderlyingType()
         {
             if (!IsActualEnum)
index 4907875..e7a64d2 100644 (file)
@@ -515,7 +515,7 @@ namespace System
             return fields[0].FieldType;
         }
 
-        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues<TEnum> instead.")]
+        [RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
         public virtual Array GetEnumValues()
         {
             if (!IsEnum)
@@ -526,6 +526,19 @@ namespace System
             throw NotImplemented.ByDesign;
         }
 
+        /// <summary>
+        /// Retrieves an array of the values of the underlying type constants in a specified enumeration type.
+        /// </summary>
+        /// <remarks>
+        /// This method can be used to get enumeration values when creating an array of the enumeration type is challenging.
+        /// For example, <see cref="T:System.Reflection.MetadataLoadContext" /> or on a platform where runtime codegen is not available.
+        /// </remarks>
+        /// <returns>An array that contains the values of the underlying type constants in enumType.</returns>
+        /// <exception cref="ArgumentException">
+        /// Thrown when the type is not an enumeration type.
+        /// </exception>
+        public virtual Array GetEnumValuesAsUnderlyingType() => throw new NotSupportedException(SR.NotSupported_SubclassOverride);
+
         [RequiresDynamicCode("The code for an array of the specified type might not be available.")]
         public virtual Type MakeArrayType() => throw new NotSupportedException();
         [RequiresDynamicCode("The code for an array of the specified type might not be available.")]
index 57341e1..e57cafb 100644 (file)
@@ -288,6 +288,38 @@ namespace System.Reflection.TypeLoading
         private volatile RoType? _lazyUnderlyingEnumType;
         public sealed override Array GetEnumValues() => throw new InvalidOperationException(SR.Arg_InvalidOperation_Reflection);
 
+#if NET7_0_OR_GREATER
+        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern",
+            Justification = "Enum Types are not trimmed.")]
+        public override Array GetEnumValuesAsUnderlyingType()
+        {
+            if (!IsEnum)
+                throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+            FieldInfo[] enumFields = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+            int numValues = enumFields.Length;
+            Array ret = Type.GetTypeCode(GetEnumUnderlyingType()) switch
+            {
+                TypeCode.Byte => new byte[numValues],
+                TypeCode.SByte => new sbyte[numValues],
+                TypeCode.UInt16 => new ushort[numValues],
+                TypeCode.Int16 => new short[numValues],
+                TypeCode.UInt32 => new uint[numValues],
+                TypeCode.Int32 => new int[numValues],
+                TypeCode.UInt64 => new ulong[numValues],
+                TypeCode.Int64 => new long[numValues],
+                _ => throw new NotSupportedException(),
+            };
+
+            for (int i = 0; i < numValues; i++)
+            {
+                ret.SetValue(enumFields[i].GetRawConstantValue(), i);
+            }
+
+            return ret;
+        }
+#endif
+
         // No trust environment to apply these to.
         public sealed override bool IsSecurityCritical => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
         public sealed override bool IsSecuritySafeCritical => throw new InvalidOperationException(SR.InvalidOperation_IsSecurity);
index b60f7cd..9f01b3b 100644 (file)
@@ -84,6 +84,9 @@ namespace SampleMetadata
     public enum EU8 : ulong { }
     public enum EI8 : long { }
 
+    public enum E_2_I4 : int { min=int.MinValue, zero=0, one=1, max=int.MaxValue}
+    public enum E_2_U4 : uint { min = uint.MinValue, zero = 0, one = 1, max = uint.MaxValue }
+
     public class GenericEnumContainer<T>
     {
         public enum GenericEnum : short { }
index c0292e2..03611db 100644 (file)
@@ -365,6 +365,30 @@ namespace System.Reflection.Tests
             }
         }
 
+#if NET7_0_OR_GREATER
+        [Fact]
+        public static void GetEnumValuesAsUnderlyingType()
+        {
+            var intEnumType = typeof(E_2_I4).Project();
+            int[] expectedIntValues = { int.MinValue, 0, 1, int.MaxValue };
+            Array intArr = intEnumType.GetEnumValuesAsUnderlyingType();
+            for (int i = 0; i < intArr.Length; i++)
+            {
+                Assert.Equal(expectedIntValues[i], intArr.GetValue(i));
+                Assert.Equal(Type.GetTypeCode(expectedIntValues[i].GetType()), Type.GetTypeCode(intArr.GetValue(i).GetType()));
+            }
+
+            var uintEnumType = typeof(E_2_U4).Project();
+            uint[] expectesUIntValues = { uint.MinValue, 0, 1, uint.MaxValue };
+            Array uintArr = uintEnumType.GetEnumValuesAsUnderlyingType();
+            for (int i = 0; i < uintArr.Length; i++)
+            {
+                Assert.Equal(expectesUIntValues[i], uintArr.GetValue(i));
+                Assert.Equal(Type.GetTypeCode(expectesUIntValues[i].GetType()), Type.GetTypeCode(uintArr.GetValue(i).GetType()));
+            }
+        }
+#endif        
+
         [Theory]
         [MemberData(nameof(GetTypeCodeTheoryData))]
         public static void GettypeCode(TypeWrapper tw, TypeCode expectedTypeCode)
index 6b553ef..3e6cd0d 100644 (file)
@@ -458,6 +458,24 @@ namespace System.Reflection.Tests
         }
 
         [Fact]
+        public static void GetEnumValuesAsUnderlyingType_Int()
+        {
+            GetEnumValuesAsUnderlyingType(typeof(IntEnum), new int[] { 1, 2, 10, 18, 45 });
+        }
+
+        [Fact]
+        public static void GetEnumValuesAsUnderlyingType_UInt()
+        {
+            GetEnumValuesAsUnderlyingType(typeof(UIntEnum), new uint[] { 1, 10 });
+        }
+
+        private static void GetEnumValuesAsUnderlyingType(Type enumType, Array expected)
+        {
+            Assert.Equal(expected, enumType.GetTypeInfo().GetEnumValuesAsUnderlyingType());
+        }
+
+
+        [Fact]
         public void GetEnumValues_TypeNotEnum_ThrowsArgumentException()
         {
             AssertExtensions.Throws<ArgumentException>("enumType", () => typeof(NonGenericClassWithNoInterfaces).GetTypeInfo().GetEnumUnderlyingType());
index 82b94e8..7f8d708 100644 (file)
@@ -2312,9 +2312,11 @@ namespace System
         public static string[] GetNames<TEnum>() where TEnum: struct, System.Enum { throw null; }
         public System.TypeCode GetTypeCode() { throw null; }
         public static System.Type GetUnderlyingType(System.Type enumType) { throw null; }
-        [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetValues<TEnum> overload instead.")]
+        [System.Diagnostics.CodeAnalysis.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 System.Array GetValues(System.Type enumType) { throw null; }
         public static TEnum[] GetValues<TEnum>() where TEnum : struct, System.Enum { throw null; }
+        public static System.Array GetValuesAsUnderlyingType(System.Type enumType) { throw null; }
+        public static System.Array GetValuesAsUnderlyingType<TEnum>() where TEnum : struct, System.Enum { throw null; }
         public bool HasFlag(System.Enum flag) { throw null; }
         public static bool IsDefined(System.Type enumType, object value) { throw null; }
         public static bool IsDefined<TEnum>(TEnum value) where TEnum : struct, System.Enum { throw null; }
@@ -5808,8 +5810,9 @@ namespace System
         public virtual string? GetEnumName(object value) { throw null; }
         public virtual string[] GetEnumNames() { throw null; }
         public virtual System.Type GetEnumUnderlyingType() { throw null; }
-        [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use Enum.GetValues<TEnum> instead.")]
+        [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("It might not be possible to create an array of the enum type at runtime. Use the GetEnumValues<TEnum> overload or the GetEnumValuesAsUnderlyingType method instead.")]
         public virtual System.Array GetEnumValues() { throw null; }
+        public virtual System.Array GetEnumValuesAsUnderlyingType() { throw null; }
         [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)]
         public System.Reflection.EventInfo? GetEvent(string name) { throw null; }
         [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents)]
index 245840e..a243e6c 100644 (file)
@@ -1589,6 +1589,76 @@ namespace System.Tests
             AssertExtensions.Throws<ArgumentNullException>("enumType", () => Enum.GetValues(null));
         }
 
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeSByteEnum_ReturnsExpected()
+        {
+            Array expected = new sbyte[] { 1, 2, sbyte.MaxValue, sbyte.MinValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(SByteEnum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<SByteEnum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeByteEnum_ReturnsExpected()
+        {
+            Array expected = new byte[] { byte.MinValue, 1, 2, byte.MaxValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(ByteEnum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<ByteEnum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeInt16Enum_ReturnsExpected()
+        {
+            Array expected = new short[] { 1, 2, short.MaxValue, short.MinValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(Int16Enum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<Int16Enum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeUInt16Enum_ReturnsExpected()
+        {
+            Array expected = new ushort[] { ushort.MinValue, 1, 2, ushort.MaxValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(UInt16Enum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<UInt16Enum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeInt32Enum_ReturnsExpected()
+        {
+            Array expected = new int[] { 1, 2, int.MaxValue, int.MinValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(Int32Enum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<Int32Enum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeUInt32Enum_ReturnsExpected()
+        {
+            Array expected = new uint[] { uint.MinValue, 1, 2, uint.MaxValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(UInt32Enum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<UInt32Enum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeInt64Enum_ReturnsExpected()
+        {
+            Array expected = new long[] { 1, 2, long.MaxValue, long.MinValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(Int64Enum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<Int64Enum>());
+        }
+
+        [Fact]
+        public void GetValuesAsUnderlyingType_InvokeUInt64Enum_ReturnsExpected()
+        {
+            Array expected = new ulong[] { ulong.MinValue, 1, 2, ulong.MaxValue };
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType(typeof(UInt64Enum)));
+            Assert.Equal(expected, Enum.GetValuesAsUnderlyingType<UInt64Enum>());
+        }
+
+        [Fact]
+        public static void GetValuesAsUnderlyingType_NullEnumType_ThrowsArgumentNullException()
+        {
+            AssertExtensions.Throws<ArgumentNullException>("enumType", () => Enum.GetValuesAsUnderlyingType(null));
+        }
+
         [Theory]
         [InlineData(typeof(object))]
         [InlineData(typeof(int))]
index 8dc2c6e..676f4e4 100644 (file)
@@ -1434,6 +1434,12 @@ internal static class ReflectionTest
                     throw new Exception("GetValues");
             }
 
+            Console.WriteLine("Enum.GetValuesAsUnderlyingType");
+            {
+                if (Enum.GetValuesAsUnderlyingType(typeof(Mine)).GetType() != typeof(int[]))
+                    throw new Exception("Enum.GetValuesAsUnderlyingType");
+            }
+
             Console.WriteLine("Pattern in LINQ expressions");
             {
                 Type objType = typeof(object);