我们能否轻松有效地找出是否设置了至少一个标志?
好吧,如果您对检查是否至少设置了一个标志位感到满意,那么是的!
用法:
if (EnumHelper.HasAnyFlagBitsSet(letter))
执行:
public static class EnumHelper
{
static EnumHelper()
{
// Required to get correct behavior in GetNumericValue
// Because we will overlap the enum type with a ulong, left-aligned
if (!BitConverter.IsLittleEndian)
throw new NotSupportedException("This type is only supported on little-endian architectures.");
}
/// <summary>
/// <para>
/// Returns whether the given enum value has any bits set that occurs in a defined flag for <typeparamref name="T"/>.
/// </para>
/// <para>
/// Throws if the type parameter is not an enum type with the <see cref="FlagsAttribute"/>.
/// </para>
/// </summary>
public static bool HasAnyFlagBitsSet<T>(T enumValue)
where T : unmanaged, Enum
{
var numericValue = GetNumericValue(enumValue);
// Take the value that has all the permitted bits set
// Use & to keep only the corresponding bits from the input value
// Check that the input value provided at least one such bit
return (numericValue & FlagValueCache<T>.AllFlagsSetValue) != 0;
}
/// <summary>
/// <para>
/// Returns whether the given enum value has any bits set that are set in <paramref name="flags"/>.
/// </para>
/// <para>
/// Throws if the type parameter is not an enum type with the <see cref="FlagsAttribute"/>.
/// </para>
/// </summary>
public static bool HasAnyFlagBitsSet<T>(T enumValue, T flags)
where T : unmanaged, Enum
{
var numericValue = GetNumericValue(enumValue);
var numericFlags = GetNumericValue(flags);
// Use & to keep only the bits present in flags
// Check that the input value provided at least one such bit
return (numericValue & flags) != 0;
}
// Actually, have a bonus method as well, since this is a common operation:
/// <summary>
/// <para>
/// Returns whether the given enum value consists exclusively of defined flags for <typeparamref name="T"/>.
/// The result is false if a bit is set that is not part of any value defined by <typeparamref name="T"/>.
/// </para>
/// <para>
/// Throws if the type parameter is not an enum type with the <see cref="FlagsAttribute"/>.
/// </para>
/// </summary>
public static bool HasDefinedFlags<T>(T enumValue)
where T : unmanaged, Enum
{
var numericValue = GetNumericValue(enumValue);
// Take the value that has all the permitted bits set
// Use ~ to get a value with all the forbidden bits set
// Use & to keep only the corresponding bits from the input value
// Check that the input value provided no such forbidden bits
return (numericValue & ~FlagValueCache<T>.AllFlagsSetValue) == 0;
}
/// <summary>
/// <para>
/// Returns the numeric value of the given <paramref name="enumValue"/>.
/// </para>
/// <para>
/// The resulting <see cref="ulong"/> can be cast to the intended integral type, even if it is a signed type.
/// </para>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong GetNumericValue<T>(T enumValue)
where T : unmanaged, Enum
{
Span<ulong> ulongSpan = stackalloc ulong[] { 0UL };
Span<T> span = MemoryMarshal.Cast<ulong, T>(ulongSpan);
span[0] = enumValue;
return ulongSpan[0];
}
/// <summary>
/// Statically caches a "full" flags value each enum type for which this class is accessed.
/// </summary>
internal static class FlagValueCache<T>
where T : unmanaged, Enum
{
/// <summary>
/// Each bit that is set in any of the type's defined values is also set in this value.
/// </summary>
public static ulong AllFlagsSetValue { get; }
static FlagValueCache()
{
if (typeof(T).BaseType != typeof(Enum)) throw new Exception("The type parameter must be an enum type.");
foreach (var value in (T[])Enum.GetValues(typeof(T)))
AllFlagsSetValue |= GetNumericValue(value);
}
}
}
我们正在检查是否设置了至少一个标志位是什么意思?
好吧,这个解决方案可能无法正确回答以下无意义的枚举:
[Flags]
public enum Nonsense
{
One = 1,
// Eh, why does this value need TWO bits when those bits are NOT defined as individual values?
TwoAndFour = 2 | 4,
}
在这里,EnumHelper.HasAnyFlagBitSet((Nonsense)2)
将 return true
,这在技术上是不正确的,因为2
它不是一个定义的标志。
但是,它对所有合理的标志枚举都非常有效,包括具有多标志的枚举:
[Flags]
public enum Fine
{
One = 1,
Two = 2,
Four = 4,
// Fine, and sensible, since these flags exist individually
TwoAndFour = 2 | 4,
}