我们常见到的位域标志定义都是基于正数的,不过其实也可以定义成基于负数的,工作起来则和正数标志位域一样。
比如定义一个8位的位域,提供两个选项分别是最高位和最低位为1。然后分别以byte和sbyte作为枚举背后数据类型,则枚举应该这样声明:
[Flags]
enum a : byte
{
//128的原码为:10000000
High = 128,
Low = 1
}
[Flags]
enum b : sbyte
{
//-128的补码为:10000000
High = -128,
Low = 1
}
接下来根据相应的类型,把变量的全部标志都设置为1,然后判断最高位和最低位是否被开启。
//将全部标志设置为1(开启状态)
//255原码:1111 1111
var aFull = (a)255;
//-1补码:1111 1111
var bFull = (b)(-1);
//通过枚举仅将最高位和最低位开启
var aMask = a.High | a.Low;
var bMask = (b.High | b.Low);
//判断标志是否被开启
Console.WriteLine((aFull & aMask) == aMask);
Console.WriteLine((bFull & bMask) == bMask);
结果都会输出True。
事实上,完全可以把它们转换成同一个类型进行同样的位操作,比如都转换成byte类型:
static void Main(string[] args)
{
//将全部标志设置为1(开启状态)
//255原码:1111 1111
var aFull = (a)255;
//-1补码:1111 1111
var bFull = (b)(-1);
doo((byte)aFull);
doo((byte)bFull);
}
static void doo(byte b)
{
//129原码:1000 0001
byte mask = 129;
Console.WriteLine((b & mask) == mask);
}
上面代码同样会输出两个True。原因是当一个负数被强制转换成无符号数字,它背后的二进制数据会被强行塞进对应变量空间,所以从位域的角度看,没有发生任何变化。(更多关于C#强制转换的越界检查可以参考这篇文章:http://www.cnblogs.com/mgen/archive/2012/04/08/2438029.html)
.NET 4.0新加的Enum.HasFlag方法的工作原理就是这样的,只不过它用的是无符号Long(ulong),如下图:
因此我们也可以写一个简单的枚举位操作辅助类型,如下:
static class EnumHelper
{
//判读是否包含指定标志位
public static bool HasFlag(long val, long flag)
{
return (val & flag) == flag;
}
//设置标志位
public static long SetFlag(long val, long flag)
{
return val | flag;
}
//取消标志位
public static long UnsetFlag(long val, long flag)
{
return val & ~flag;
}
}