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
Changes from 12 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
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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)) :
@@ -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);
}
@@ -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)
{
32 changes: 31 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Enum.cs
Original file line number Diff line number Diff line change
@@ -315,13 +315,43 @@ 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>
/// Gets an array of values of the underlying type of the Enum.
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <typeparam name="TEnum">Enum type</typeparam>
/// /// <remarks>
/// This method can be used to get enum values when creating an Array of Enum is challenging
/// For example, reflection-only context or on a platform where runtime codegen is not available.
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
/// </remarks>
/// <returns>Array of values containing the underlying type of the Enum</returns>
public static Array GetValuesAsUnderlyingType<TEnum>() where TEnum : struct, Enum =>
typeof(TEnum).GetEnumValuesAsUnderlyingType();

/// <summary>
/// Gets an array of values of the underlying type of the Enum.
/// </summary>
/// <param name="enumType">Enum type</param>
/// <remarks>
/// This method can be used to get enum values when creating an Array of Enum is challenging
/// For example, in reflection-only context or on a platform where runtime codegen is not available.
/// </remarks>
/// <returns>Array of values containing the underlying type of the Enum</returns>
/// <exception cref="ArgumentNullException">
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
/// Thrown when the enum type is null
/// </exception>
public static Array GetValuesAsUnderlyingType(Type enumType)
{
ArgumentNullException.ThrowIfNull(enumType);
return enumType.GetEnumValuesAsUnderlyingType();
}

[Intrinsic]
public bool HasFlag(Enum flag)
{
Original file line number Diff line number Diff line change
@@ -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);
102 changes: 101 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/RuntimeType.cs
Original file line number Diff line number Diff line change
@@ -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)
@@ -139,6 +139,106 @@ public override Array GetEnumValues()
return ret;
}

/// <summary>
/// Gets an array of values of the underlying type of the Enum.
/// </summary>
/// <remarks>
/// This method can be used to get enum values when creating an Array of Enum is challenging
/// For example, in reflection-only context or on a platform where runtime codegen is not available.
/// </remarks>
/// <returns>Array of values containing the underlying type of the Enum</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 NotImplementedException("Not Implemented");
LakshanF marked this conversation as resolved.
Show resolved Hide resolved
}
}

public override Type GetEnumUnderlyingType()
{
if (!IsActualEnum)
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
@@ -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)
@@ -526,6 +526,19 @@ public virtual Array GetEnumValues()
throw NotImplemented.ByDesign;
}

/// <summary>
/// Gets an array of values of the underlying type of the Enum.
/// </summary>
/// <remarks>
/// This method can be used to get enum values when creating an Array of Enum is challenging
/// For example, in reflection-only context or on a platform where runtime codegen is not available.
/// </remarks>
/// <returns>Array of values containing the underlying type of the Enum</returns>
/// <exception cref="ArgumentException">
/// Thrown when the type is not Enum
/// </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.")]
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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 { }
Original file line number Diff line number Diff line change
@@ -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)
18 changes: 18 additions & 0 deletions src/libraries/System.Reflection/tests/TypeInfoTests.cs
Original file line number Diff line number Diff line change
@@ -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()
{
7 changes: 5 additions & 2 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
@@ -2312,9 +2312,11 @@ protected Enum() { }
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.")]
jkotas marked this conversation as resolved.
Show resolved Hide resolved
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; }
@@ -5860,8 +5862,9 @@ protected Type() { }
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)]
Loading