[英]How to check if any flags of a flag combination are set?
Let's say I have this enum:假设我有这个枚举:
[Flags]
enum Letters
{
A = 1,
B = 2,
C = 4,
AB = A | B,
All = A | B | C,
}
To check if for example AB
is set I can do this:要检查是否设置了AB
,我可以这样做:
if((letter & Letters.AB) == Letters.AB)
Is there a simpler way to check if any of the flags of a combined flag constant are set than the following?是否有比以下方法更简单的方法来检查是否设置了组合标志常量的任何标志?
if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)
Could one for example swap the &
with something?例如,可以用一些东西交换&
吗?
In .NET 4 you can use the Enum.HasFlag method :在 .NET 4 中,您可以使用Enum.HasFlag 方法:
using System;
[Flags] public enum Pet {
None = 0,
Dog = 1,
Cat = 2,
Bird = 4,
Rabbit = 8,
Other = 16
}
public class Example
{
public static void Main()
{
// Define three families: one without pets, one with dog + cat and one with a dog only
Pet[] petsInFamilies = { Pet.None, Pet.Dog | Pet.Cat, Pet.Dog };
int familiesWithoutPets = 0;
int familiesWithDog = 0;
foreach (Pet petsInFamily in petsInFamilies)
{
// Count families that have no pets.
if (petsInFamily.Equals(Pet.None))
familiesWithoutPets++;
// Of families with pets, count families that have a dog.
else if (petsInFamily.HasFlag(Pet.Dog))
familiesWithDog++;
}
Console.WriteLine("{0} of {1} families in the sample have no pets.",
familiesWithoutPets, petsInFamilies.Length);
Console.WriteLine("{0} of {1} families in the sample have a dog.",
familiesWithDog, petsInFamilies.Length);
}
}
The example displays the following output:该示例显示以下输出:
// 1 of 3 families in the sample have no pets.
// 2 of 3 families in the sample have a dog.
If you want to know if letter has any of the letters in AB you must use the AND &
operator.如果您想知道 letter 是否包含 AB 中的任何字母,则必须使用AND &
运算符。 Something like:就像是:
if ((letter & Letters.AB) != 0)
{
// Some flag (A,B or both) is enabled
}
else
{
// None of them are enabled
}
I use extension methods to write things like that :我使用扩展方法来写这样的东西:
if (letter.IsFlagSet(Letter.AB))
...
Here's the code :这是代码:
public static class EnumExtensions
{
private static void CheckIsEnum<T>(bool withFlags)
{
if (!typeof(T).IsEnum)
throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
}
public static bool IsFlagSet<T>(this T value, T flag) where T : struct
{
CheckIsEnum<T>(true);
long lValue = Convert.ToInt64(value);
long lFlag = Convert.ToInt64(flag);
return (lValue & lFlag) != 0;
}
public static IEnumerable<T> GetFlags<T>(this T value) where T : struct
{
CheckIsEnum<T>(true);
foreach (T flag in Enum.GetValues(typeof(T)).Cast<T>())
{
if (value.IsFlagSet(flag))
yield return flag;
}
}
public static T SetFlags<T>(this T value, T flags, bool on) where T : struct
{
CheckIsEnum<T>(true);
long lValue = Convert.ToInt64(value);
long lFlag = Convert.ToInt64(flags);
if (on)
{
lValue |= lFlag;
}
else
{
lValue &= (~lFlag);
}
return (T)Enum.ToObject(typeof(T), lValue);
}
public static T SetFlags<T>(this T value, T flags) where T : struct
{
return value.SetFlags(flags, true);
}
public static T ClearFlags<T>(this T value, T flags) where T : struct
{
return value.SetFlags(flags, false);
}
public static T CombineFlags<T>(this IEnumerable<T> flags) where T : struct
{
CheckIsEnum<T>(true);
long lValue = 0;
foreach (T flag in flags)
{
long lFlag = Convert.ToInt64(flag);
lValue |= lFlag;
}
return (T)Enum.ToObject(typeof(T), lValue);
}
public static string GetDescription<T>(this T value) where T : struct
{
CheckIsEnum<T>(false);
string name = Enum.GetName(typeof(T), value);
if (name != null)
{
FieldInfo field = typeof(T).GetField(name);
if (field != null)
{
DescriptionAttribute attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
}
}
If you can use .NET 4 or higher than use HasFlag() method如果您可以使用 .NET 4 或更高版本而不是使用 HasFlag() 方法
examples例子
letter.HasFlag(Letters.A | Letters.B) // both A and B must be set
same as如同
letter.HasFlag(Letters.AB)
If it really annoys you, you can write a function like that:如果它真的让你烦恼,你可以写一个这样的函数:
public bool IsSet(Letters value, Letters flag)
{
return (value & flag) == flag;
}
if (IsSet(letter, Letters.A))
{
// ...
}
// If you want to check if BOTH Letters.A and Letters.B are set:
if (IsSet(letter, Letters.A & Letters.B))
{
// ...
}
// If you want an OR, I'm afraid you will have to be more verbose:
if (IsSet(letter, Letters.A) || IsSet(letter, Letters.B))
{
// ...
}
To check if for example AB is set I can do this:要检查是否设置了 AB,我可以这样做:
if((letter & Letters.AB) == Letters.AB)如果((字母和字母.AB)==字母.AB)
Is there a simpler way to check if any of the flags of a combined flag constant are set than the following?是否有比以下更简单的方法来检查是否设置了组合标志常量的任何标志?
This checks that both A and B are set, and ignores whether any other flags are set.这个检查是A和B两者都设置,以及是否忽略任何其他标志被设置。
if((letter & Letters.A) == Letters.A || (letter & Letters.B) == Letters.B)
This checks that either A or B is set, and ignores whether any other flags are set or not.即A或B被设定此检查,是否忽略任何其他标志被设定或没有。
This can be simplified to:这可以简化为:
if(letter & Letters.AB)
Here's the C for binary operations;这是用于二元运算的 C; it should be straightforward to apply this to 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));
Incidentally, the naming of the variable in the question's example is the singular 'letter', which might imply that it represents only a single letter;顺便提一下,问题示例中变量的命名是单数“字母”,这可能意味着它只代表一个字母; the example code makes it clear that its a set of possible letters and that multiple values are allowed, so consider renaming the variable 'letters'.示例代码清楚地表明它是一组可能的字母并且允许多个值,因此请考虑重命名变量“字母”。
I created a simple extension method that does not need a check on Enum
types:我创建了一个不需要检查Enum
类型的简单扩展方法:
public static bool HasAnyFlag(this Enum value, Enum flags)
{
return
value != null && ((Convert.ToInt32(value) & Convert.ToInt32(flags)) != 0);
}
It also works on nullable enums.它也适用于可为空的枚举。 The standard HasFlag
method does not, so I created an extension to cover that too.标准的HasFlag
方法没有,所以我创建了一个扩展来覆盖它。
public static bool HasFlag(this Enum value, Enum flags)
{
int f = Convert.ToInt32(flags);
return
value != null && ((Convert.ToInt32(value) & f) == f);
}
A simple test:一个简单的测试:
[Flags]
enum Option
{
None = 0x00,
One = 0x01,
Two = 0x02,
Three = One | Two,
Four = 0x04
}
[TestMethod]
public void HasAnyFlag()
{
Option o1 = Option.One;
Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
Assert.AreEqual(false, o1.HasFlag(Option.Three));
o1 |= Option.Two;
Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
Assert.AreEqual(true, o1.HasFlag(Option.Three));
}
[TestMethod]
public void HasAnyFlag_NullableEnum()
{
Option? o1 = Option.One;
Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
Assert.AreEqual(false, o1.HasFlag(Option.Three));
o1 |= Option.Two;
Assert.AreEqual(true, o1.HasAnyFlag(Option.Three));
Assert.AreEqual(true, o1.HasFlag(Option.Three));
}
Enjoy!享受!
There are a lot of answers on here but I think the most idiomatic way to do this with Flags would be Letters.AB.HasFlag(letter) or (Letters.A | Letters.B).HasFlag(letter) if you didn't already have Letters.AB.这里有很多答案,但我认为使用 Flags 最惯用的方法是 Letters.AB.HasFlag(letter) 或 (Letters.A | Letters.B).HasFlag(letter) 如果你没有已经有 Letters.AB。 letter.HasFlag(Letters.AB) only works if it has both. letter.HasFlag(Letters.AB) 只有在两者都有时才有效。
How about怎么样
if ((letter & Letters.AB) > 0)
? ?
这对你有用吗?
if ((letter & (Letters.A | Letters.B)) != 0)
You can use this extension method on enum, for any type of enums:您可以在枚举上使用此扩展方法,用于任何类型的枚举:
public static bool IsSingle(this Enum value)
{
var items = Enum.GetValues(value.GetType());
var counter = 0;
foreach (var item in items)
{
if (value.HasFlag((Enum)item))
{
counter++;
}
if (counter > 1)
{
return false;
}
}
return true;
}
if((int)letter != 0) { }
You could just check if the value is not zero.您可以检查该值是否不为零。
if ((Int32)(letter & Letters.AB) != 0) { }
But I would consider it a better solution to introduce a new enumeration value with value zero and compare agains this enumeration value (if possible because you must be able to modify the enumeration).但我认为引入一个值为零的新枚举值并再次比较该枚举值是一个更好的解决方案(如果可能,因为您必须能够修改枚举)。
[Flags]
enum Letters
{
None = 0,
A = 1,
B = 2,
C = 4,
AB = A | B,
All = AB | C
}
if (letter != Letters.None) { }
UPDATE更新
Missread the question - fixed the first suggestion and just ignore the second suggestion.误读了问题 - 修正了第一个建议,而忽略了第二个建议。
There are two aproaches that I can see that would work for checking for any bit being set.我可以看到有两种方法可以用于检查是否设置了任何位。
Aproach A方法A
if (letter != 0)
{
}
This works as long as you don't mind checking for all bits, including non-defined ones too!只要您不介意检查所有位(包括未定义的位),此方法就有效!
Aproach B方法B
if ((letter & Letters.All) != 0)
{
}
This only checks the defined bits, as long as Letters.All represents all of the possible bits.这仅检查定义的位,只要 Letters.All 代表所有可能的位。
For specific bits (one or more set), use Aproach B replacing Letters.All with the bits that you want to check for (see below).对于特定位(一组或多组),使用方法 B 将 Letters.All 替换为您要检查的位(见下文)。
if ((letter & Letters.AB) != 0)
{
}
从 .Net 4 开始,您可以使用速记版本而无需明确指定 &:
if(Letters.AB.HasFlag(Letters.C))
Can we find out easily and efficiently whether at least one flag is set ?我们能否轻松高效地找出是否至少设置了一个标志?
Well, if you are satisfied with checking whether at least one flag bit is set , then yes!好吧,如果您对检查是否至少设置了一个标志位感到满意,那么是的!
Usage:用法:
if (EnumHelper.HasAnyFlagBitsSet(letter))
Implementation:执行:
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);
}
}
}
What does it mean that we are checking if at least one flag bit is set?我们正在检查是否至少设置了一个标志位是什么意思?
Well, this solution may fail to answer correctly for nonsensical enums like the following:嗯,这个解决方案可能无法正确回答如下无意义的枚举:
[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,
}
Here, EnumHelper.HasAnyFlagBitSet((Nonsense)2)
would return true
, which is technically incorrect, since 2
is not a defined flag.在这里, EnumHelper.HasAnyFlagBitSet((Nonsense)2)
将返回true
,这在技术上是不正确的,因为2
不是定义的标志。
However, it works perfectly fine for all sensible flags enums, including ones with multi-flags:但是,它对所有合理的标志枚举都非常有效,包括具有多标志的枚举:
[Flags]
public enum Fine
{
One = 1,
Two = 2,
Four = 4,
// Fine, and sensible, since these flags exist individually
TwoAndFour = 2 | 4,
}
Sorry, but i will show it in VB :)抱歉,但我会在 VB 中显示它 :)
<Flags()> Public Enum Cnt As Integer
None = 0
One = 1
Two = 2
Three = 4
Four = 8
End Enum
Sub Test()
Dim CntValue As New Cnt
CntValue += Cnt.One
CntValue += Cnt.Three
Console.WriteLine(CntValue)
End Sub
CntValue = 5 So the enum contains 1 + 4 CntValue = 5 所以枚举包含 1 + 4
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.