我需要检测是否在枚举值中设置了一个标志,哪个类型用Flag属性标记。
通常是这样的:
(value & flag) == flag
但是因为我需要通用泛型(有时在运行时我的事件只有一个“Enum”引用。我找不到一个简单的方法来使用&amp;运算符。目前我这样做:< / p>
public static bool IsSet<T>(this T value, T flags) where T : Enum
{
Type numberType = Enum.GetUnderlyingType(typeof(T));
if (numberType.Equals(typeof(int)))
{
return BoxUnbox<int>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(sbyte)))
{
return BoxUnbox<sbyte>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(byte)))
{
return BoxUnbox<byte>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(short)))
{
return BoxUnbox<short>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(ushort)))
{
return BoxUnbox<ushort>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(uint)))
{
return BoxUnbox<uint>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(long)))
{
return BoxUnbox<long>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(ulong)))
{
return BoxUnbox<ulong>(value, flags, (a, b) => (a & b) == b);
}
else if (numberType.Equals(typeof(char)))
{
return BoxUnbox<char>(value, flags, (a, b) => (a & b) == b);
}
else
{
throw new ArgumentException("Unknown enum underlying type " + numberType.Name + ".");
}
}
private static bool BoxUnbox<T>(object value, object flags, Func<T, T, bool> op)
{
return op((T)value, (T)flags);
}
但是我不喜欢永无止境的if - else块,所以有没有办法来投射这些值,我可以使用&amp;运营商或任何其他解决方案来检查这个?
答案 0 :(得分:15)
对我来说,它看起来过于复杂。这个怎么样(请记住,枚举总是映射到整数值类型):
public static bool IsSet<T>(T value, T flags) where T : struct
{
// You can add enum type checking to be perfectly sure that T is enum, this have some cost however
// if (!typeof(T).IsEnum)
// throw new ArgumentException();
long longFlags = Convert.ToInt64(flags);
return (Convert.ToInt64(value) & longFlags) == longFlags;
}
答案 1 :(得分:10)
我为枚举编写了一组扩展方法,以备不时之需:
public static class EnumExtensions
{
private static void CheckEnumWithFlags<T>()
{
if (!typeof(T).IsEnum)
throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
if (!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
{
CheckEnumWithFlags<T>();
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
{
CheckEnumWithFlags<T>();
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
{
CheckEnumWithFlags<T>();
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
{
CheckEnumWithFlags<T>();
long lValue = 0;
foreach (T flag in flags)
{
long lFlag = Convert.ToInt64(flag);
lValue |= lFlag;
}
return (T)Enum.ToObject(typeof(T), lValue);
}
}
主要缺点是您无法指定where T : Enum
:它是明确禁止的(“约束不能是特殊类'System.Enum'”),因此扩展方法将出现在所有结构的intellisense中。 ..我添加了CheckEnumWithFlags
方法来检查该类型实际上是枚举,并具有Flags
属性。
更新:Jon Skeet最近创建了一个名为UnconstrainedMelody的有趣库,它完成了同样的事情,并且解决了上面提到的泛型类型约束限制
答案 2 :(得分:7)
这应该适用于任何基础类型的enum
类型:
public static bool IsSet<T>(this T value, T flags) where T : struct
{
return (Convert.ToInt64(value) & Convert.ToInt64(flags)) ==
Convert.ToInt64(flags);
}
使用 Convert.ToInt64
是因为64位整数是可能的“最宽”整数类型,所有枚举值都可以转换为(即使ulong
)。 请注意在C#中似乎不有效,但 一般在CIL / CLR中有效。char
不是有效的基础类型。
此外,您不能为枚举强制执行通用类型约束(即where T : struct
);您可以做的最好的事情是使用where T : struct
强制T
为值类型,然后可选地执行动态检查以确保T
是枚举类型。
为了完整起见,这是我非常简短的测试工具:
static class Program
{
static void Main(string[] args)
{
Debug.Assert(Foo.abc.IsSet(Foo.abc));
Debug.Assert(Bar.def.IsSet(Bar.def));
Debug.Assert(Baz.ghi.IsSet(Baz.ghi));
}
enum Foo : int
{
abc = 1,
def = 10,
ghi = 100
}
enum Bar : sbyte
{
abc = 1,
def = 10,
ghi = 100
}
enum Baz : ulong
{
abc = 1,
def = 10,
ghi = 100
}
}
答案 3 :(得分:6)
就个人而言,我认为看起来很好,因为你把它包装成一个单一用途的功能。如果你将这些代码分散在整个程序中,我认为你会遇到一些问题,但你所创建的内容会提高所使用的清晰度,而且功能本身就足够清晰。
当然是我的意见。
你可以使用is关键字,这可能有点帮助
public static bool IsSet<T>(this T value, T flags) where T : Enum
{
if (value is int)
{
return ((int)(object)a & (int)(object)b) == (int)(object)b);
}
//etc...
答案 4 :(得分:2)
只需使用 Enum.HasFlag()方法!
答案 5 :(得分:0)
我用它来比较标志
public static bool IsSet<T>(this T input, T match)
{
return (Convert.ToUInt32(input) & Convert.ToUInt32(match)) != 0;
}
您可以在这里进行不同的转化。从int到short到long。
答案 6 :(得分:0)
...或 public static bool IsSet(此枚举值,枚举比较) { int baseValue = value.ToInt32(); int compareValue = compare.ToInt32(); if(baseValue == 0) 返回false; return((baseValue&amp; compareValue)== compareValue); }