假设我有这样一个enum:

[Flags]
enum Letters
{
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = A | B | C,
}

为了检查AB是否被设置,我可以这样做:

if((letter & Letters.AB) == Letters.AB)

有没有一种比下面更简单的方法来检查是否设置了一个组合标志常量的任何标志?

if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)

例如,可以将&与其他东西交换吗?


当前回答

如果你想知道字母AB中是否有字母,你必须使用AND &运算符。喜欢的东西:

if ((letter & Letters.AB) != 0)
{
    // Some flag (A,B or both) is enabled
}
else
{
    // None of them are enabled
}

其他回答

有两种方法,我可以看到,将工作检查任何位被设置。

阿普罗赫·

if (letter != 0)
{
}

只要你不介意检查所有位,包括未定义的位,这就可以工作!

阿普罗赫·

if ((letter & Letters.All) != 0)
{
}

这只检查定义的位,只要字母。All表示所有可能的位。

对于特定的位(一个或多个集),使用方法B替换字母。所有这些都是你想要检查的(见下文)。

if ((letter & Letters.AB) != 0)
{
}

我们能否轻松有效地找出是否至少设置了一个标志?

好吧,如果您对检查是否至少设置了一个标志位感到满意,那么是的!

用法:

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((无意义)2)将返回true,这在技术上是不正确的,因为2不是一个定义的标志。

然而,对于所有敏感的标志枚举,包括带有多个标志的枚举,它工作得非常好:

[Flags]
public enum Fine
{
    One = 1,
    Two = 2,
    Four = 4,

    // Fine, and sensible, since these flags exist individually
    TwoAndFour = 2 | 4,
}

为了检查AB是否被设置,我可以这样做: if((字母和字母。ab) ==字母。ab) 有没有一种比下面更简单的方法来检查是否设置了一个组合标志常量的任何标志?

这将检查是否设置了A和B,并忽略是否设置了任何其他标志。

if((字母和字母。a) ==字母。||(字母和字母。b) ==字母。b)

这将检查是否设置了A或B,并忽略是否设置了任何其他标志。

这可以简化为:

if(letter & Letters.AB)

这是二进制运算的C;应用到c#应该很简单:

enum {
     A = 1,
     B = 2,
     C = 4,
     AB = A | B,
     All = AB | C,
};

int flags = A|C;

bool anything_and_a = flags & A;

bool only_a = (flags == A);

bool a_and_or_c_and_anything_else = flags & (A|C);

bool both_ac_and_anything_else = (flags & (A|C)) == (A|C);

bool only_a_and_c = (flags == (A|C));

顺便提一下,问题示例中变量的命名是单数字母“letter”,这可能暗示它只代表一个字母;示例代码清楚地表明,它是一组可能的字母,并且允许多个值,因此可以考虑将变量重命名为“letters”。

if((int)letter != 0) { }

你可以检查这个值是否不为零。

if ((Int32)(letter & Letters.AB) != 0) { }

但是我认为引入一个值为0的新枚举值并与该枚举值进行比较是更好的解决方案(如果可能的话,因为您必须能够修改枚举)。

[Flags]
enum Letters
{
    None = 0,
    A    = 1,
    B    = 2,
    C    = 4,
    AB   =  A | B,
    All  = AB | C
}

if (letter != Letters.None) { }

更新

看错了问题——修正了第一个建议,忽略第二个建议。