Here's two ways you can do it without Expression compiling:
First is using unsafe code:
public class EnumUtil
{
private static unsafe ulong EnumBitsAsUlong<T>(T value) where T : unmanaged, Enum
{
ReadOnlySpan<byte> valueBytes = new(&value, sizeof(T));
ulong valueUlong = 0;
valueBytes.CopyTo(new Span<byte>(&valueUlong, sizeof(ulong)));
return valueUlong;
}
public static T AllFlagsViaUnsafe<T>() where T : unmanaged, Enum
{
// any enum fits in an ulong
ulong result = 0;
var values = Enum.GetValues<T>();
foreach (var value in values)
{
result |= EnumBitsAsUlong(value);
}
return Unsafe.As<ulong, T>(ref result);
}
}
EDIT: THIS ONE BELOW DOESN'T WORK! It doesn't work because enums don't implement IBitwiseOperators even though they should IMO. I'm keeping it here because in the future the needed interfaces might be implemented on enums.
Second is by constraining T to be able to use the bitwise or operator:
public class EnumUtil
{
public static T AllFlagsViaBitwise<T>() where T : unmanaged, Enum, IBitwiseOperators<T, T, T>
{
return Enum.GetValues<T>().Aggregate((a, b) => a | b);
}
}
Obviously the second way is better if your C# version supports it
EDIT: Adding versions of code below which throws exceptions when the Enum is not in fact a flags Enum
EDIT: THE ONE WITH IBitwiseOperators DOESN'T WORK! It doesn't work because enums don't implement IBitwiseOperators even though they should IMO. I'm keeping it here because in the future the needed interfaces might be implemented on enums.
public class EnumUtil
{
private static unsafe ulong EnumBitsAsUlong<T>(T value) where T : unmanaged, Enum
{
ReadOnlySpan<byte> valueBytes = new(&value, sizeof(T));
ulong valueUlong = 0;
valueBytes.CopyTo(new Span<byte>(&valueUlong, sizeof(ulong)));
return valueUlong;
}
public static T AllFlagsViaUnsafe<T>() where T : unmanaged, Enum
{
// any enum fits in an ulong
ulong result = 0;
foreach (var value in Enum.GetValues<T>())
{
var valueUlong = EnumBitsAsUlong(value);
if ((result & valueUlong) != 0)
{
throw new ArgumentException(
$"{typeof(T).Name} is not a flags enum. Detected at enum value {value}", nameof(T));
}
result |= EnumBitsAsUlong(value);
}
return Unsafe.As<ulong, T>(ref result);
}
public static T AllFlagsViaBitwise<T>() where T : unmanaged, Enum, IBitwiseOperators<T, T, T>, IEqualityOperators<T, T, bool>
{
T result = default;
foreach (var value in Enum.GetValues<T>())
{
if ((result & value) != default)
{
throw new ArgumentException(
$"{typeof(T).Name} is not a flags enum. Detected at enum value {value}", nameof(T));
}
result |= value;
}
return result;
}
}