代码之家  ›  专栏  ›  技术社区  ›  simendsjo

为什么[flag]的枚举从0开始并递增1?

  •  8
  • simendsjo  · 技术社区  · 14 年前

    编辑:似乎大多数人都误解了我的问题。

    我知道枚举是如何工作的,我知道二进制。我想知道 为什么 具有[flags]属性的枚举按原样设计。

    原始职位:

    这可能是一个副本,但我没有找到任何其他帖子,所以这里是。

    我敢打赌,它背后有一些很好的理由,我只是觉得它有点容易出错。

    [Flag]
    public enum Flagged
    {
      One, // 0
      Two, // 1
      Three, // 2
      Four, // 3
    }
    
    Flagged f; // Defaults to Flagged.One = 0
    f = Flagged.Four;
    (f & Flagged.One) != 0; // Sure.. One defaults to 0
    (f & Flagged.Two) != 0; // 3 & 1 == 1
    (f & Flagged.Three) != 0; // 3 & 2 == 2
    

    如果它做了这样的事,会不会更有意义?

    [Flag]
    public enum Flagged
    {
      One = 1 << 0, // 1
      Two = 1 << 1, // 2
      Three = 1 << 2, // 4
      Four = 1 << 3, // 8
    }
    
    Flagged f; // Defaults to 0
    f = Flagged.Four;
    (f & Flagged.One) != 0; // 8 & 1  == 0
    (f & Flagged.Two) != 0; // 8 & 2 == 0
    (f & Flagged.Three) != 0; // 8 & 4 == 0
    (f & Flagged.Four) != 0; // 8 & 8 == 8
    

    当然。。我不太确定它应该如何处理这样的自定义标志

    [Flag]
    public enum Flagged
    {
       One, // 1
       Two, // 2
       LessThanThree = One | Two,
       Three, // 4? start from Two?
       LessThanFour = Three | LessThanThree,
       Three, // 8? start from Three?
    }
    

    规范给出了一些指导原则

    以2的幂定义枚举常量,即1、2、4、8等。这意味着组合枚举常量中的各个标志不重叠。

    但这可能是自动完成的,因为我打赌你永远不会希望我的第一个例子出现。请启发我:)

    6 回复  |  直到 10 年前
        1
  •  6
  •   BC.    14 年前

    属性是[旗]而不是[旗],没有什么神奇的。它唯一可能影响的是ToString方法。当指定[Flags]时,值以逗号分隔。由您指定值,使其在位字段中有效。

        2
  •  12
  •   Brian Rasmussen    14 年前

    这个 Flags 属性仅用于将值格式化为多个值。位操作在具有或不具有属性的基础类型上工作。

        3
  •  7
  •   Jeff Yates    14 年前

    除非显式地给出其他值,否则枚举的第一项为零。对于标志枚举,最好是使用零值,因为它为零值提供了语义意义,例如“无标志”或“关闭”。这有助于维护代码,因为它可能暗示代码中的意图(尽管注释也可以实现这一点)。

    除此之外,是否需要零值还取决于您和您的设计。

    因为标记枚举仍然只是枚举 FlagsAttribute 只指示调试器将这些值解释为其他值的组合),枚举中的下一个值总是比前一个值多一个。因此,在指定位值时应该显式,因为您可能希望将组合表示为其他值的位或。

    也就是说,想象一个要求所有按位组合的标记枚举的语法放在枚举定义的末尾,或者以某种方式标记,这样编译器就知道如何处理其他一切,这是不合理的。

    例如(假设 flags 关键词,我们在北半球,

    flags enum MyFlags
    {
       January,
       February,
       March,
       April,
       May,
       June,
       July,
       August,
       September,
       October,
       November,
       December,
       Winter = January | February | March
       Spring = April | May | June
       Summer = July | August | September
       Autumn = October | November | December
    } 
    

    使用这种语法,编译器可以自己创建0值,并自动为其他值分配标志。

        4
  •  4
  •   Jon Skeet    14 年前

    注释的C 3规范中没有任何内容,我想是这样的。 可以 做注释C 4规范中的某件事-我不确定。(我想我自己开始写这样的注释,但后来删除了它。)

    对于简单的情况来说,这是可以的,但是一旦你开始添加额外的标志,它就会变得有点棘手:

    [Flags]
    enum Features
    {
        Frobbing, // 1
        Blogging, // 2 
        Widgeting, // 4
        BloggingAndWidgeting = Frobbing | Blogging, // 6
        Mindnumbing // ?
    }
    

    麻木应该有什么价值?下一个未使用的位?如果设置一个固定的整数值呢?

    我同意这是一种痛苦。也许可以制定一些合理的规则…但我想知道复杂性与价值平衡是否真的能解决问题。

        5
  •  2
  •   Powerlord    14 年前

    简单地说, Flags 是一个属性。它直到创建枚举之后才应用,因此不会更改分配给枚举的值。

    这么说之后,msdn页面 Designing Flags Enumerations 这样说:

    一个旗子要用两个的力量 枚举的值,以便 使用按位或自由组合 操作。

    重要提示:如果您不使用两个或 两个幂的组合,按位 操作将无法按预期工作。

    同样,页面 FlagsAttribute

    在powers中定义枚举常量 其中两个是1,2,4,8等等 在。这意味着 组合枚举常量不 重叠。

        6
  •  0
  •   supercat    14 年前

    在C语言中,可以(AB)使用预处理器自动生成两个枚举的幂。如果有一个宏make-thing s扩展到“make-thing(flag1)make-thing(flag2)make-thing(flag3)”等,则可以多次调用该宏,使用不同的make-thing定义,从而实现两个标志名序列以及其他一些优点的幂。

    例如,首先将make_thing(x)定义为“linear_enum__x”(包括逗号),然后使用enum语句生成枚举列表(包括make_things宏外部的linear_num_items)。然后创建另一个枚举,这次将make_thing(x)定义为“flag_enum__x=1<

    相当漂亮的一些可以这样做的事情,带有标志和线性值自动保持同步;代码可以做一些很好的事情,比如“if(thingie[linear_enum_foo]thing_errors=flag_enum_foo;”(同时使用线性值和标志值)。不幸的是,我不知道在C或VB.NET中做任何远程类似的事情。