2009-09-10 61 views
20

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

它遍布.NET框架。不知道这就是最好的例子,但在.NET框架有以下几点:

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

所以给出一个锚的风格,我们可以使用位掩码来找出哪些国家的选择。但是,您似乎可以使用为每个可能的值定义的bool属性或单个枚举值数组的AnchorStyle类/结构完成相同的操作。

当然,我的问题的主要原因是,我想知道如果我应该按照我自己的代码类似的做法。

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

  • 内存消耗较少? (它不是似乎喜欢它会消耗比bools的数组/结构更少)
  • 比结构或数组更好的堆栈/堆性能?
  • 更快的比较操作?更快的增值/删除?
  • 对于编写它的开发者来说更​​方便吗?
+2

不是说我发现了一个强有力的论点,但它确实消耗了更少的内存。它使用一个int(4字节),而每个布尔使用一个字节。所以,4个布尔使用相同的一个int。 32个布尔最多使用32个字节,而所有这些布尔可以使用相同的枚举。如果你去不推荐的路径,你可以使枚举长8个字节(sizeof(long))。 – 2009-09-10 17:16:41

+0

感谢您澄清。它引导我到这篇文章:http://stackoverflow.com/questions/294905/why-in-net-system-boolean-takes-4-byte – 2009-09-10 21:38:54

+0

所以从响应很明显,枚举标志比结构更轻量级/内存方面的布尔数组。但是,似乎也有一些.NET框架类可以很好地适用于该任务,例如BitVector32或BitArray。如何使用BitVector32(由uint支持)进行存储并提供在特定索引处获取/设置位(如bools)的属性? Windows窗体似乎这样做。为开发人员提供更多代码,但看起来它会表现良好,并且封装将使下游API消费者更容易使用。嗯? – 2009-09-10 21:40:58

回答

13

这是传统上减少内存使用的方法。所以,是的,它比较陈旧的C#:-)

作为一种编程技术,它可能会在当前系统已经过时了,你会是相当好的使用布尔变量数组,但...

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

它使用的内存要少得多。将所有4个示例值放入一个位掩码将使用半个字节。使用一个布尔数组,最有可能会使用几个字节的数组对象加上每个布尔长字。如果你必须存储一百万个值,你会明白为什么一个位掩码版本更好。

这是比较容易管理,你只需要对付一个整数值,而布尔变量数组将存储在完全不同,说的数据库。

而且,由于存储器的布局,在比阵列每一个方面要快得多。它几乎与使用单个32位整数一样快。我们都知道,就数据操作而言,速度一样快。

1

这是为了速度和效率。基本上所有你正在使用的是一个int。

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

这是一个非常高层次的答案。你能具体说明哪些操作更快/更高效,为什么?或者链接到一篇证明你的说法合理的文章? – 2009-09-10 17:20:26

+0

我真的需要给你证明,使用本机类型和简单的逻辑表达式是快速和高效的工作吗? – ChaosPandion 2009-09-10 17:25:53

+0

不要忘记操作顺序。你必须在这里按位操作放置括号。 – 2009-09-10 17:54:24

12
  • 易以任意顺序设置多个标志。

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

+2

请注意,即使作为单独的列,SQL Server也会将它们优化为单个字节:http://msdn.microsoft.com/en-us/library/ms177603.aspx – AaronLS 2013-01-16 23:44:51

6

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

+0

在我看来,向类中添加布尔值是如: bool newState; 关于复制,复制一个结构似乎很容易。 – 2009-09-10 17:25:12

+0

@Winston:序列化格式发生变化,以及为旧数据接受默认值的良好序列化程序,以及旧版本不丢弃未知字段的情况很难找到。二进制接口发生了变化,这可能会导致一连串所需的更新,并且需要对该结构进行全面的维护支持。 (当然,*合约*必须明确地陈述“未知位被忽略”或“未知位导致错误”)。而且,在实施层面上,整体处理更容易。 – peterchen 2009-09-10 17:36:51

+1

@Winston如果您创建了一个API,该怎么办?然后,所有可能升级到新版本的人都必须更改代码,因为新的bool已添加到方法中。而如果它是一个枚举,那么就不需要做任何改变来保持使用相同的代码。这就是为什么。NET框架有利于布尔值的枚举。 – 2009-09-10 17:46:00

3

事实上,它可以有更好的性能,主要是如果你的枚举派生自一个字节。 在这种极端情况下,每个枚举值将由一个字节表示,其中包含所有组合,最多为256个。具有如此多的可能的组合布尔值将导致256个字节。

但即使如此,我并不认为这是真正的原因。我更喜欢这些的原因是C#让我处理这些枚举的能力。我可以使用单个表达式添加多个值。我也可以删除它们。我甚至可以使用枚举将单个表达式同时比较多个值。通过布尔运算符,代码可以变成更冗长的代码。

+3

有256个组合,但只有8个标志。不要混淆他们。 – Dykam 2009-09-10 17:33:48

+0

对不起,我的英文不够清晰。你是对的。 – 2009-09-10 18:56:12

+1

256组合使用bool?它是8个bool值。 8个bool值不是256个字节。 – CoperNick 2014-02-11 12:40:14

5

它也可以使方法更清晰。想象一下有10个布尔与1个位掩码的方法。

2

我绝不会用枚举标志,除非你正在处理一些非常严重的内存限制(不太可能)建议。你应该总是编写优化维护的代码。

有几个布尔属性使得它更容易阅读和理解的代码,更改值,并提供智能感知评论更不用说减少错误的可能性。如果有必要,你可以在内部总是使用枚举标志字段,只要确保你暴露了设置/获取值的布尔属性。

3

Raymond Chen有a blog post on this subject

当然,位字段保存数据存储,但 你必须平衡它针对代码大小,可调试和 减少多线程的 成本。

正如其他人所说,它的时间在很大程度上是过去。它仍然很有诱惑力,有点让人觉得很有趣,很酷,但它不再更高效,它在维护方面有严重的缺陷,它不能很好地与数据库一起玩,除非你在嵌入式世界,你有足够的记忆。

+2

雷蒙德谈论的是位域,而不是位掩码。 – gbjbaanb 2009-09-11 13:30:22

1

从域模型的角度来看,它只是现实的模型在某些情况下更好。如果您有三个布尔类型,如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. 空间效率 - 1位
  2. 时间效率 - 位比较是由硬件快速处理。
  3. 语言独立性 - 数据可能由多个不同的程序处理,您无需担心跨不同语言/平台的布尔值实现。

大多数时候,这些都不值得在维修方面进行折中。然而,有些时候它是有用的:

  1. 网络协议 - 将有降低的消息
  2. 旧版软件的大小大的储蓄 - 一旦我不得不添加一些信息追踪到一些旧的软件。

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

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

+1

1.空间效率只适用于密集包装或非常有限的环境; 2.时间效率取决于掩码的有效使用(并且掩盖和比较单个位肯定不比*比较单个布尔值更快); 3.不适用,错误地使用布尔类型错误地使用布尔类型。 – user2864740 2014-10-09 23:19:02

相关问题