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 enum
s 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 enum
s.
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 enum
s 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 enum
s.
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;
}
}