Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new API GetEnumValuesAsUnderlyingType #73057

Merged
merged 18 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ public sealed override Array GetEnumValues()
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(
/// </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)) :
Expand All @@ -85,7 +85,7 @@ private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, Cult
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);
}
Expand Down Expand Up @@ -171,14 +171,14 @@ private static long GetEnumValue(bool isUnderlyingTypeUInt64, Enum enumVal, Cult
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)
{
Expand Down
35 changes: 34 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,46 @@ public static TEnum[] GetValues<TEnum>() where TEnum : struct, Enum =>
(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">
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
/// 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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public sealed override Type MakeArrayType(int rank)
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);
Expand Down
103 changes: 102 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public override string[] GetEnumNames()
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)
Expand All @@ -139,6 +139,107 @@ public override Array GetEnumValues()
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)))
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
{

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)
Expand Down
15 changes: 14 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ public virtual Type GetEnumUnderlyingType()
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)
Expand All @@ -526,6 +526,19 @@ public virtual Array GetEnumValues()
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.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,38 @@ public sealed override Type MakeArrayType(int rank)
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()
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
{
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ public enum EI4 : int { }
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 { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,30 @@ public static IEnumerable<object[]> GetEnumUnderlyingTypeData
}
}

#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()));
}
}
jkotas marked this conversation as resolved.
Show resolved Hide resolved
#endif

[Theory]
[MemberData(nameof(GetTypeCodeTheoryData))]
public static void GettypeCode(TypeWrapper tw, TypeCode expectedTypeCode)
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.Reflection/tests/TypeInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,24 @@ private static void GetEnumValues(Type enumType, Array expected)
Assert.Equal(expected, enumType.GetTypeInfo().GetEnumValues());
}

[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()
{
Expand Down
Loading