为什么使用flags + bitmasks而不是一系列布尔?

鉴于我有一个可能处于一个或多个真/假状态的对象的情况,我总是有点模糊为什么程序员经常使用标志+位掩码而不是仅仅使用几个布尔值。

它遍布.NET框架。 不确定这是否是最好的示例,但.NET框架具有以下内容:

public enum AnchorStyles { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8 } 

因此,给定锚样式,我们可以使用位掩码来确定选择哪个状态。 但是,似乎您可以使用AnchorStyle类/结构完成相同的操作,其中为每个可能的值定义bool属性,或者为单个枚举值的数组定义。

当然,我的问题的主要原因是我想知道我是否应该使用自己的代码进行类似的练习。

那么,为什么要用这种方法呢?

  • 减少内存消耗? (它似乎不会消耗少于bools的数组/结构)
  • 比结构或数组更好的堆栈/堆性能?
  • 更快的比较操作? 更快的增值/删除?
  • 编写它的开发人员更方便吗?

它传统上是一种减少内存使用的方法。 所以,是的,它在C#中相当陈旧:–)

作为一种编程技术,它在今天的系统中可能已经过时了,你可以使用一系列bool,但是……

比较存储为位掩码的值是快速的。 使用AND和OR逻辑运算符并比较生成的2个整数。

它使用相当少的内存。 将所有4个示例值放在位掩码中将使用半个字节。 使用bool数组,很可能会为数组对象使用几个字节,为每个bool使用一个长字。 如果你必须存储一百万个值,你就会明白为什么bitmask版本更优越。

它更容易管理,你只需要处理一个整数值,而一个bool数组将存储完全不同,比如数据库。

而且,由于内存布局,每个方面都比arrays快得多。 它几乎与使用单个32位整数一样快。 我们都知道,对于数据操作而言,速度最快。

  • 轻松设置任何顺序的多个标志。

  • 易于保存并获得数据库的0101011系列。

除此之外,它更容易向位域添加新的位含义而不是向类添加新的布尔值。 从一个实例到另一个实例,比一系列布尔值更容易复制位域。

它还可以使方法更清晰。 想象一个方法,10个bool对比1个Bitmask。

实际上,它可以有更好的性能,主要是因为你的枚举来自一个字节。 在这种极端情况下,每个枚举值将由一个字节表示,包含所有组合,最多256个。具有这么多可能的布线组合将导致256个字节。

但是,即便如此,我认为这不是真正的原因。 我更喜欢那些的原因是C#让我处理那些枚举的力量。 我可以用一个表达式添加几个值。 我也可以删除它们。 我甚至可以使用枚举一次比较几个值。 对于布尔值,代码可以变得更加冗长。

Raymond Chen有一篇关于这个主题的博客文章 。

当然,位域保存数据存储器,但您必须在代码大小,可调试性和减少multithreading方面的成本之间取得平衡。

正如其他人所说,其时间已基本结束。 它仍然很容易做到这一点,因为有点摆弄是有趣和冷静的,但它不再更有效率,它在维护方面有严重的缺点,它不能很好地与数据库,并除非你在一个工作嵌入式世界,你有足够的记忆力。

我建议永远不要使用枚举标志,除非你正在处理一些非常严重的内存限制(不太可能)。 您应该始终编写为维护而优化的代码。

具有多个布尔属性使得更容易阅读和理解代码,更改值,并提供智能感知评论,更不用说减少错误的可能性。 如有必要,您可以在内部始终使用枚举标志字段,只需确保使用布尔属性公开值的设置/获取。

从领域模型的角度来看,它只是在某些情况下更好地模拟现实。 如果您有三个布尔值,如AccountIsInDefault和IsPreferredCustomer以及RequiresSalesTaxState,那么将它们添加到单个Flags修饰枚举中是没有意义的,因为它们不是同一域模型元素的三个不同值。

但是,如果你有一组像这样的布尔值:

  [Flags] enum AccountStatus {AccountIsInDefault=1, AccountOverdue=2 and AccountFrozen=4} 

要么

  [Flags] enum CargoState {ExceedsWeightLimit=1, ContainsDangerousCargo=2, IsFlammableCargo=4, ContainsRadioactive=8} 

然后,能够将账户(或货物)的总状态存储在一个变量中是有用的…表示一个域元素,其值可以表示任何可能的状态组合。

  1. 空间效率 – 1位
  2. 时间效率 – 比特由硬件快速处理。
  3. 语言独立性 – 数据可能由许多不同的程序处理,您不必担心跨不同语言/平台实施布尔值。

大多数时候,这些都不值得在维护方面进行权衡。 但是,有时它很有用:

  1. 网络协议 – 减少消息大小将大大节省
  2. 遗留软件 – 一旦我不得不添加一些信息来追踪一些遗留软件。

修改标题的成本:数百万美元和多年的努力。 将信息塞进标头中未使用的2个字节的成本:0。

当然,访问和操作这些信息的代码还有额外的成本,但这些都是由函数完成的,所以一旦你定义了访问器,它就不比使用布尔值那样可维护了。

这是为了提高速度和效率。 基本上你所使用的只是一个int。

 if ((flags & AnchorStyles.Top) == AnchorStyles.Top) { //Do stuff }