C# 为什么要使用标志+;比特面具而不是一系列的布尔人?

C# 为什么要使用标志+;比特面具而不是一系列的布尔人?,c#,enums,flags,C#,Enums,Flags,如果我有一个对象可能处于一个或多个真/假状态,我总是有点不明白为什么程序员经常使用标志+位掩码,而不仅仅是使用几个布尔值 它遍布.NET框架。不确定这是否是最好的示例,但.NET framework具有以下特点: public enum AnchorStyles { None = 0, Top = 1, Bottom = 2, Left = 4, Right = 8 } 因此,给定锚定样式,我们可以使用位掩码来确定选择了哪些状态。但是,似乎您可以通过为每

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

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

public enum AnchorStyles
{
    None = 0,
    Top = 1,
    Bottom = 2,
    Left = 4,
    Right = 8
}
因此,给定锚定样式,我们可以使用位掩码来确定选择了哪些状态。但是,似乎您可以通过为每个可能的值定义bool属性的AnchorStyle类/结构或单个枚举值的数组来完成同样的事情

当然,我提出这个问题的主要原因是我想知道我是否应该在自己的代码中遵循类似的实践

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

  • 更少的内存消耗?(它似乎不会消耗少于一个布尔数组/布尔结构)
  • 比结构或数组更好的堆栈/堆性能
  • 更快的比较操作?更快的增值/增值
  • 对编写它的开发人员来说更方便

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

if ((flags & AnchorStyles.Top) == AnchorStyles.Top)
{
    //Do stuff
} 
  • 以任意顺序轻松设置多个标志

  • 易于保存,并将0101011系列数据保存到数据库中


除其他外,向位字段添加新的位含义比向类添加新的布尔值更容易。与一系列布尔值相比,将一个位字段从一个实例复制到另一个实例更容易

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


但是,即使这样,我也不认为这是真正的原因。我之所以喜欢这些,是因为C#赋予我处理这些枚举的能力。我可以用一个表达式添加多个值。我也可以移除它们。我甚至可以使用enum使用单个表达式一次比较多个值。有了布尔值,代码可以变得,比方说,更加冗长。

这是传统上减少内存使用的一种方式。所以,是的,它在C中已经过时了

作为一种编程技术,它在今天的系统中可能已经过时,您可以使用一系列布尔值,但是

比较存储为位掩码的值很快。使用AND或逻辑运算符并比较结果2整数

它使用的内存要少得多。将所有4个示例值放入位掩码将使用半个字节。使用bool数组时,很可能会对数组对象使用几个字节,对每个bool使用一个长单词。如果您必须存储一百万个值,您将确切地看到为什么位掩码版本更优越

它更易于管理,只需处理单个整数值,而布尔数组在数据库中的存储方式则截然不同


而且,由于内存布局,在各个方面都比阵列快得多。它几乎和使用单个32位整数一样快。我们都知道,这是数据操作的最快速度。

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

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

拥有多个布尔属性可以更容易地阅读和理解代码、更改值和提供Intellisense注释,更不用说降低bug的可能性了。如有必要,您可以始终在内部使用enum标志字段,只需确保使用布尔属性公开值的设置/获取。

Raymond Chen有

当然,位域可以节省数据内存,但是 你必须使它与环境保持平衡 代码大小、可调试性和 减少了多线程


正如其他人所说,它的时代基本上已经过去了。仍然这样做很有诱惑力,因为小摆弄既有趣又酷,但它不再高效,它在维护方面存在严重缺陷,不能很好地处理数据库,除非你在嵌入式世界中工作,否则你有足够的内存。

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

但是如果你有一组布尔值,比如:

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

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

  • 空间效率-1位
  • 时间效率-位比较由硬件快速处理
  • 语言独立性-数据可以由多个不同的程序处理,您无需担心布尔值在不同语言/平台上的实现
  • 在大多数情况下,这些都不值得在维护方面进行权衡。但是,有时它是有用的:

  • 网络协议-减少消息的大小将大大节省开支
  • 遗留软件-有一次我不得不添加一些信息,以便跟踪到遗留软件中 修改标题的成本:数百万美元和多年的努力。 将信息硬塞入标头中未使用的2个字节的成本:0

    当然,在访问和操作这些信息的代码中会有额外的成本,但是这些都是由函数完成的,所以一旦定义了访问器,它的可维护性就不亚于
      [Flags] enum CargoState {ExceedsWeightLimit=1,  
             ContainsDangerousCargo=2, IsFlammableCargo=4, 
             ContainsRadioactive=8}