[.NET] Fix enum from/to Variant conversion

- `typeof(T).IsEnum` and `typeof(T).GetEnumUnderlyingType()` are intrinsified in .NET 8+ so we can simplify the code in VariantUtils a bit.
- Consider whether enum `T` is signed in VariantUtils to preserve it.
This commit is contained in:
Raul Santos 2025-07-11 19:54:45 +02:00
parent d7cc121e64
commit 8950744921
No known key found for this signature in database
GPG key ID: B532473AE3A803E4

View file

@ -196,28 +196,38 @@ public partial class VariantUtils
if (typeof(GodotObject).IsAssignableFrom(typeof(T)))
return CreateFromGodotObject(UnsafeAs<GodotObject>(from));
// `typeof(T).IsValueType` is optimized away
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
// `typeof(T).IsEnum` is optimized away
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
if (typeof(T).IsEnum)
{
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
// We don't need to know whether it's signed or unsigned.
// `typeof(T).GetEnumUnderlyingType()` is optimized away
if (Unsafe.SizeOf<T>() == 1)
var enumType = typeof(T).GetEnumUnderlyingType();
if (enumType == typeof(sbyte))
return CreateFromInt(UnsafeAs<sbyte>(from));
if (Unsafe.SizeOf<T>() == 2)
if (enumType == typeof(short))
return CreateFromInt(UnsafeAs<short>(from));
if (Unsafe.SizeOf<T>() == 4)
if (enumType == typeof(int))
return CreateFromInt(UnsafeAs<int>(from));
if (Unsafe.SizeOf<T>() == 8)
if (enumType == typeof(long))
return CreateFromInt(UnsafeAs<long>(from));
if (enumType == typeof(byte))
return CreateFromInt(UnsafeAs<byte>(from));
if (enumType == typeof(ushort))
return CreateFromInt(UnsafeAs<ushort>(from));
if (enumType == typeof(uint))
return CreateFromInt(UnsafeAs<uint>(from));
if (enumType == typeof(ulong))
return CreateFromInt(UnsafeAs<ulong>(from));
throw UnsupportedType<T>();
}
@ -387,28 +397,38 @@ public partial class VariantUtils
if (typeof(GodotObject).IsAssignableFrom(typeof(T)))
return (T)(object)ConvertToGodotObject(variant);
// `typeof(T).IsValueType` is optimized away
// `typeof(T).IsEnum` is NOT optimized away: https://github.com/dotnet/runtime/issues/67113
// Fortunately, `typeof(System.Enum).IsAssignableFrom(typeof(T))` does the job!
// `typeof(T).IsEnum` is optimized away
if (typeof(T).IsValueType && typeof(System.Enum).IsAssignableFrom(typeof(T)))
if (typeof(T).IsEnum)
{
// `Type.GetTypeCode(typeof(T).GetEnumUnderlyingType())` is not optimized away.
// Fortunately, `Unsafe.SizeOf<T>()` works and is optimized away.
// We don't need to know whether it's signed or unsigned.
// `typeof(T).GetEnumUnderlyingType()` is optimized away
if (Unsafe.SizeOf<T>() == 1)
var enumType = typeof(T).GetEnumUnderlyingType();
if (enumType == typeof(sbyte))
return UnsafeAsT(ConvertToInt8(variant));
if (Unsafe.SizeOf<T>() == 2)
if (enumType == typeof(short))
return UnsafeAsT(ConvertToInt16(variant));
if (Unsafe.SizeOf<T>() == 4)
if (enumType == typeof(int))
return UnsafeAsT(ConvertToInt32(variant));
if (Unsafe.SizeOf<T>() == 8)
if (enumType == typeof(long))
return UnsafeAsT(ConvertToInt64(variant));
if (enumType == typeof(byte))
return UnsafeAsT(ConvertToUInt8(variant));
if (enumType == typeof(ushort))
return UnsafeAsT(ConvertToUInt16(variant));
if (enumType == typeof(uint))
return UnsafeAsT(ConvertToUInt32(variant));
if (enumType == typeof(ulong))
return UnsafeAsT(ConvertToUInt64(variant));
throw UnsupportedType<T>();
}