【问题标题】:Why should I never use 0 in a flag enum [duplicate]为什么我永远不应该在标志枚举中使用 0 [重复]
【发布时间】:2012-03-21 18:56:53
【问题描述】:

可能重复:
Should an Enum start with a 0 or a 1?

为什么我不应该在标志枚举中使用 0?我已经读过很多遍了,我想 知道原因:)

【问题讨论】:

  • 我想知道你在哪里读到的以及你为什么这么说......
  • 除了表示一个未设置的值外,你不应该使用它
  • 您应该只将它用于None 选项。
  • @ChrisShain 不,不是。把你的事实弄清楚。 1&2 为 0。

标签: c# enums flags


【解决方案1】:

为什么我不应该在标志枚举中使用 0?

这个问题是基于一个不正确的假设。您应该始终在标志枚举中使用零。它应始终设置为“无”。

除了表示“未设置任何标志”之外,您不应将其用于任何其他用途。

为什么不呢?

因为如果零具有“无”以外的含义,它会变得非常混乱。人们有合理的期望,((e & E.X) == E.X) 的意思是“X 标志设置了吗?”但如果 X 为零,则此表达式将始终为真,即使 逻辑上标志未“设置”。

【讨论】:

    【解决方案2】:

    因为标志枚举是位集合或选项集。

    0 值将是所有集合的一部分,或者不属于任何集合。它只是行不通。

    【讨论】:

    • 明确的None 命名值有时会很有用。
    • 阐述@Henk 的出色答案:AnyNumber AND 0 将始终等于 0,并且 0 与任何数字的 ORing 必须对数字产生影响(将始终等于自身)
    【解决方案3】:

    虽然零表示没有设置任何位,但为 0 指定一个命名常量通常非常有用。

    当我设置标志字时,我定义了位的名称,以便它们都代表非默认值。也就是说,枚举值始终初始化为零,从而“关闭”位域表示的所有选项。

    这为您的枚举提供了前向兼容性,因此创建新值的任何人都知道,如果您以后向位域添加更多标志,任何零位都将是“安全”的。

    同样,将多个标志组合成一个新的常量名称也非常有用,这使代码更具可读性。

    这样做的危险(以及您引用规则的原因)只是您必须了解单个位值(标志)和表示位组或位组合的值之间的差异。

    【讨论】:

      【解决方案4】:

      标志枚举是这样使用的:

      Flag flags = Flag.First | Flag.Next | Flag.Last;
      

      那么你应该像这样定义你的标志:

      enum Flag {First = 1, Next = 2, Last = 4};
      

      这样你就可以看到一个标志是否被使用了,例如:

      if (flags & Flag.First <> 0) Console.WriteLine("First is set");
      if (flags & Flag.Next <> 0) Console.WriteLine("Next is set");
      if (flags & Flag.Last <> 0) Console.WriteLine("Last is set");
      

      这就是为什么您只能使用 2 的幂的值,例如1,2,4,8,16,32,64,128,...

      如果 flags 为 0,则视为空白。

      我希望这会增加你对标志枚举的理解。

      【讨论】:

      • 从技术上讲,您不必使用 2 的幂的值。例如,值 1、10、100 也可以正常工作(因为它们的位不重叠)。但没有理由这样做。
      【解决方案5】:

      因为通常您使用如下标志:

      var myFlagEnum = MyEnum.Foo | MyEnum.Bar | MyEnum.Bat;
      
      // ...  snip ...
      
      if (myFlagEnum & MyEnum.Foo == MyEnum.Foo) { ...  do something ... };
      

      如果 MyEnum.Foo 为零,则上述内容将不起作用(在所有情况下都将返回 true)。而如果它是 1,那么它会起作用。

      【讨论】:

      • 我相信您打算使用按位运算符 &amp; 而不是逻辑与运算符 &amp;&amp;
      • 对,对不起,不应该在 SO 窗口中编码
      【解决方案6】:

      一个标志枚举假定它的每个值都代表一个选项的存在,并且它被编码在枚举的一个位中。因此,如果存在特定选项(或为真),则枚举值中的 equiveland 位设置为 (1),否则不设置 (0)

      所以枚举的每个字段都是一个只设置了一个位的值。如果没有任何选项存在或为真,则组合枚举的值为零,这意味着没有设置任何位。因此,标志枚举中唯一的零字段应该意味着没有设置、为真或选择任何选项。

      例如,假设我们有一个标志枚举,它编码表格单元格中边框的存在

      public enum BorderType
      {
          None   = 0x00, // 00000000 in binary
          Top    = 0x01, // 00000001 in binary
          Left   = 0x02, // 00000010 in binary
          Right  = 0x04, // 00000100 in binary
          Bottom = 0x08  // 00001000 in binary
      }
      

      如果你想显示一个单元格有上下边框,那么你应该使用值

       Cell.Border = BorderType.Top | BorderType.Bottom; // 0x01 | 0x08 = 0x09 = 00001001 in binary
      

      如果你想显示一个单元格没有边框,那么你应该使用值

       Cell.Border = BorderType.None; // 0x00 = 00000000 in binary
      

      所以你不应该在标志枚举中使用零作为选项的值,但你应该始终使用零作为值,这意味着没有设置任何标志。

      【讨论】:

        【解决方案7】:

        我真的没有看到问题。

        enum Where {Nowhere=0x00, Left=0x01, Right=0x02, Both=Left|Right};
        Where thevalue = Where.Both;
        bool result = (thevalue&Where.Nowhere)==Where.Nowhere;
        

        当然结果是真的!你期待什么?在这里,考虑一下。

        bool result1 = (thevalue&Where.Left)==Where.Left;
        bool result2 = (thevalue&Where.Right)==Where.Right;
        bool result3 = (thevalue&Where.Both)==Where.Both;
        

        这些都是真的!为什么Nowhere 要特别? 0没什么特别的!

        【讨论】:

        • A) OP 是关于标志枚举的。您只会看到类似标志的行为,因为枚举的默认实现是分配递增整数,并且最多可以使用 4 个条目(并且仅当第四个是第二个和第三个的并集时)。尝试添加第五个值。 B) 没有什么特别之处,因为nowhere &amp; left == nowhere,而left &amp; right == both
        • 好的,如果你愿意,我会拼出位值。不过,这并没有什么区别,你知道的。而且您对&amp; 的工作方式仍然有误。查找它,或在您自己的编译器上测试它。 left &amp; right 真的是 0。老实说。
        • 问题是你将Both定义为Both=left|Right,而不是Both=Nowhere|Left|Right。所以当 thevalue = Where.Both but (thevalue&Where.Nowhrere)==Where.Nowhere 返回 true 时,就违背了使用这个枚举来表示多选的目的。如果您从 0x01 开始,则不会发生这种情况。可能您应该使用不同的示例上下文而不是您使用的示例上下文进行思考。一周中的几天进行更改。
        • @MrLister 将它们定义为标志有什么问题?如果这样做,您可以使用同一个 Enum 的值来表示多天。
        • 在这种情况下,您的情况与上述相同。星期日 = 位 0,星期一 = 位 1 等等。然后可以将值“从不”表示为值 0,即没有设置位。
        猜你喜欢
        • 1970-01-01
        • 2011-06-29
        • 1970-01-01
        • 1970-01-01
        • 2014-05-14
        • 1970-01-01
        • 2015-12-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多