假设我有这样一个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)
例如,可以将&与其他东西交换吗?
你可以检查这个值是否不为零。
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) { }
更新
看错了问题——修正了第一个建议,忽略第二个建议。
我创建了一个简单的扩展方法,不需要检查Enum类型:
public static bool HasAnyFlag(this Enum value, Enum flags)
{
return
value != null && ((Convert.ToInt32(value) & Convert.ToInt32(flags)) != 0);
}
它也适用于可为空的枚举。标准的HasFlag方法没有,所以我创建了一个扩展来覆盖它。
public static bool HasFlag(this Enum value, Enum flags)
{
int f = Convert.ToInt32(flags);
return
value != null && ((Convert.ToInt32(value) & f) == f);
}
一个简单的测试:
[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));
}
享受吧!
我使用扩展方法来写这样的东西:
if (letter.IsFlagSet(Letter.AB))
...
代码如下:
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;
}
}
如果你觉得很烦,你可以这样写一个函数:
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))
{
// ...
}