一个byte表示多个bool属性的功能
如何实现用一个byte表示多个bool属性的功能?
用一个byte表示多个bool属性,本质是利用二进制位(bit) 的特性 ——1 个byte包含 8 个二进制位(bit0~bit7),每个位可以表示一个bool值(0 表示false,1 表示true)。这种方式能极大节省内存,尤其适合需要存储大量bool属性的场景(比如你的Pixel类)。
一、核心原理:把 byte 当 “8 个开关的面板”
想象一个byte是一个有 8 个开关的面板,每个开关只有 “开(1)” 和 “关(0)” 两种状态:
第 1 个开关(bit0):控制bo属性;
第 2 个开关(bit1):控制mark属性;
第 3 个开关(bit2):控制isLamp属性;
... 以此类推,最多可以控制 8 个bool属性。
例如:byte值为 5(二进制是 00000101),表示:
bit0=1 → bo=true;
bit1=0 → mark=false;
bit2=1 → isLamp=true;
其他位 = 0 → 对应属性为false。
二、具体实现:用位运算操作 “开关”
需要 4 个核心位运算操作:置位(开)、清位(关)、查位(读状态)、反转位(切换状态)。下面结合代码示例说明(以Pixel类的bo、mark、isLamp三个属性为例):
步骤 1:定义位标记(哪个位对应哪个属性)
先给每个bool属性分配一个 “位索引”(0~7),用常量表示:
csharp
public struct Pixel
{
// 定义每个属性对应的位(0~7)
private const byte BoBit = 0; // bit0 → 对应bo属性
private const byte MarkBit = 1; // bit1 → 对应mark属性
private const byte IsLampBit = 2; // bit2 → 对应isLamp属性
// 用一个byte存储所有位状态(8个开关的总状态)
private byte _flags; // 核心存储变量,初始值0(所有位为0)
}
步骤 2:实现 “查位”(读取 bool 值)
判断某个位是否为 1(true),用与运算(&):
csharp
public struct Pixel
{
// 读取bo属性:判断bit0是否为1
public bool bo => (_flags & (1 << BoBit)) != 0;
// 读取mark属性:判断bit1是否为1
public bool mark => (_flags & (1 << MarkBit)) != 0;
// 读取isLamp属性:判断bit2是否为1
public bool isLamp => (_flags & (1 << IsLampBit)) != 0;
// 其他代码...
}
原理:1 << n 会生成一个 “第 n 位为 1,其他位为 0” 的数字(比如1 << 2是100二进制 = 4),与_flags做&运算后,如果结果非 0,说明第 n 位是 1。
步骤 3:实现 “置位”(设置为 true)
把某个位设为 1(true),用或运算(|):
csharp
public struct Pixel
{
// 设置bo为true:将bit0置为1
public void SetBoTrue() => _flags |= (byte)(1 << BoBit);
// 设置mark为true:将bit1置为1
public void SetMarkTrue() => _flags |= (byte)(1 << MarkBit);
// 设置isLamp为true:将bit2置为1
public void SetIsLampTrue() => _flags |= (byte)(1 << IsLampBit);
// 其他代码...
}
原理:1 << n 生成第 n 位为 1 的数字,与_flags做|运算后,第 n 位会被强制设为 1(其他位不变)。
步骤 4:实现 “清位”(设置为 false)
把某个位设为 0(false),用与非运算(&~):
csharp
public struct Pixel
{
// 设置bo为false:将bit0清为0
public void SetBoFalse() => _flags &= (byte)~(1 << BoBit);
// 设置mark为false:将bit1清为0
public void SetMarkFalse() => _flags &= (byte)~(1 << MarkBit);
// 设置isLamp为false:将bit2清为0
public void SetIsLampFalse() => _flags &= (byte)~(1 << IsLampBit);
// 其他代码...
}
原理:~(1 << n) 生成 “第 n 位为 0,其他位为 1” 的数字,与_flags做&运算后,第 n 位会被强制设为 0(其他位不变)。
步骤 5:实现 “反转位”(true→false 或 false→true)
切换某个位的状态,用异或运算(^):
csharp
public struct Pixel
{
// 切换bo的状态(true→false,false→true)
public void ToggleBo() => _flags ^= (byte)(1 << BoBit);
// 其他属性同理...
}
原理:1 << n 与_flags做^运算后,第 n 位会反转(0→1,1→0),其他位不变。
三、使用示例:操作多个 bool 属性
csharp
// 创建一个Pixel实例
Pixel p = new Pixel();
// 设置属性
p.SetBoTrue(); // bo=true(bit0=1)
p.SetMarkTrue(); // mark=true(bit1=1)
p.SetIsLampFalse(); // isLamp=false(bit2=0)
// 此时_flags的二进制是 00000011(十进制3)
// 读取属性
Console.WriteLine(p.bo); // 输出:True
Console.WriteLine(p.mark); // 输出:True
Console.WriteLine(p.isLamp); // 输出:False
// 切换状态
p.ToggleBo(); // bo从true→false(bit0=0)
Console.WriteLine(p.bo); // 输出:False
四、优势与注意事项
优势:
节省内存:1 个byte存储 8 个bool(原本 8 个bool占 8 字节,现在只需 1 字节,节省 87.5%);
提高效率:CPU 处理位运算速度极快,且byte占用内存小,缓存利用率更高。
注意事项:
最多 8 个属性:1 个byte只能存 8 个bool,超过需要用ushort(16 位)、uint(32 位)等;
调试稍复杂:直接打印_flags是数字(如 3),需转换为二进制查看(可用Convert.ToString(_flags, 2));
封装必要性:必须通过方法(如SetBoTrue)操作,避免直接修改_flags导致逻辑混乱。
总结
用 1 个byte表示多个bool,本质是 “用二进制位当开关”,通过位运算(&、|、~、^)操作这些开关。这种方式在需要大量bool属性的场景(如你的像素类)中非常实用,既能节省内存,又能提升性能。