[英]Comparing enum flags in C#
I need to detect if a flag is set within an enum value, which type is marked with the Flag attribute. 我需要检测是否在枚举值中设置了标志,该类型用Flag属性标记。
Usually it is made like that: 通常是这样制作的:
(value & flag) == flag
But since I need to do this by generic (sometimes at runtime I event have only an "Enum" reference. I can not find an easy way to use the & operator. At the moment I make it like this: 但是由于我需要通过通用方式执行此操作(有时在运行时,我的事件只有一个“ Enum”引用。我找不到使用&运算符的简便方法。此刻,我将其设置为:
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);
}
But I don't like the never ending if - else blocks, so is there a way to cast these values that I can use the & operator or any other solution to check this? 但是我不喜欢永无止境的if-else块,因此有没有一种方法可以强制使用&运算符或任何其他解决方案来检查这些值的值?
For me it looks overcomplicated. 对我来说,它看起来太复杂了。 How about this (keeping in mind that enum is always mapped to an integer value type):
如何处理(请记住,枚举始终映射为整数类型):
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;
}
I wrote a set of extension methods for enums, in case you need it : 我为枚举编写了一组扩展方法,以防您需要:
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);
}
}
The main drawback is that you can't specify where T : Enum
: it is explicitly forbidden ("Constraint cannot be special class 'System.Enum'"), so the extension methods will appear in intellisense for all structs... I added the CheckEnumWithFlags
method to check that the type is actually an enum, and has the Flags
attribute. 主要缺点是您不能指定
where T : Enum
:明确禁止where T : Enum
使用(“约束不能是特殊类'System.Enum'”),因此扩展方法将在所有结构的intellisense中出现。 CheckEnumWithFlags
方法,以检查类型实际上是枚举,并具有Flags
属性。
UPDATE : Jon Skeet recently started an interesting library called UnconstrainedMelody which does exactly the same sort of things, and works around the generic type constraint limitation mentioned above 更新:乔恩·斯凯特(Jon Skeet)最近启动了一个有趣的库,名为UnconstrainedMelody ,该库可以执行完全相同的操作,并且可以解决上述通用类型约束限制
This should do the job for enum
types with any underlying types: 这应该对具有任何基础类型的
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
is used because a 64-bit integer is the "widest" integral type possible, to which all enum values can be cast (even ulong
). 之所以使用
Convert.ToInt64
是因为64位整数是可能的“最大”整数类型,所有枚举值都可以转换为整数(甚至ulong
)。 Note that char
is not a valid underlying type.请注意, It seems that it is not valid in C#, but it is in general valid in CIL/for the CLR. char
不是有效的基础类型。看来,这是不是在C#有效的,但它是在一般有效的CIL /为CLR。
Also, you can't enforce a generic type constraint for enums (ie where T : struct
); 另外,您不能对枚举强制使用通用类型约束(即
where T : struct
); the best you can do is use where T : struct
to enforce T
to be a value type, and then optionally perform a dynamic check to ensure that T
is an enum type. 最好的办法是使用
where T : struct
将T
强制为值类型,然后有选择地执行动态检查以确保T
为枚举类型。
For completeness, here is my very brief test harness: 为了完整起见,这是我非常简短的测试工具:
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
}
}
Personally, I think that look fine because you've wrapped it into a single purpose function. 就个人而言,我认为它看起来不错,因为您已将其包装到一个单一目的的函数中。 If you had that code scattered through an entire program I think you would have some problems, but what you've created improves clarity everywhere it is used and the function itself is clear enough what it does.
如果您将代码分散在整个程序中,我认为您会遇到一些问题,但是您创建的内容可以在使用该代码的地方提高清晰度,而函数本身也很清楚其功能。
Just my opinion of course. 当然只是我的意见。
You could though, use the is keyword, which might help a little 不过,您可以使用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...
只需使用Enum.HasFlag()方法!
I have used this to compare flags 我用它来比较标志
public static bool IsSet<T>(this T input, T match)
{
return (Convert.ToUInt32(input) & Convert.ToUInt32(match)) != 0;
}
Here you can do the different conversions. 您可以在这里进行不同的转换。 From int to short to long.
从int到short到long。
or... public static bool IsSet(this Enum value, Enum compare) { int baseValue = value.ToInt32(); 或...公共静态布尔IsSet(此Enum值,Enum比较){int baseValue = value.ToInt32(); int compareValue = compare.ToInt32();
int compareValue = compare.ToInt32(); if (baseValue == 0) return false;
如果(baseValue == 0)返回false; return ((baseValue & compareValue) == compareValue);
return((baseValue&compareValue)== compareValue); }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.