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

时间:2010-06-24 17:03:06

标签: c# enums bitflags

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

我知道枚举是如何运作的,我知道二进制。我想知道为什么带有[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 个答案:

答案 0 :(得分:12)

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

答案 1 :(得分:7)

除非明确给出其他值,否则枚举的第一项为零。通常最佳做法是为标志枚举设置零值,因为它为零值提供了语义含义,例如“无标志”或“关闭”。这可能有助于维护代码,因为它可能意味着代码意图(尽管注释也实现了这一点)。

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

由于标志枚举仍然只是枚举(FlagsAttribute仅指示调试器将值解释为其他值的组合),枚举中的下一个值总是比前一个值多一个。因此,您应该明确指定位值,因为您可能希望将组合表示为其他值的按位或。

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

例如(假设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值,并自动为其他值分配标志。

答案 2 :(得分:6)

属性是[Flags]而不是[Flag],并没有什么神奇之处。它似乎唯一影响的是ToString方法。指定[标志]时,值以逗号分隔。由您指定值以使其在位字段中有效。

答案 3 :(得分:4)

注释的C#3规范中没有任何内容。我认为可能是带注释的C#4规范中的东西 - 我不确定。 (我想我自己开始编写这样的注释,但后来将其删除了。)

对于简单的情况很好,但是一旦你开始添加额外的标志,它就会有点棘手:

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

Mindnumbing应该有什么价值?下一个没用过的位?如果你设置一个固定的整数值怎么样?

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

答案 4 :(得分:2)

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

话虽如此,MSDN页面Designing Flags Enumerations说明了这一点:

  

对标志使用2的幂   枚举的值,所以它们可以   使用按位OR自由组合   操作

     

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

同样,FlagsAttribute的页面显示

  

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

答案 5 :(得分:0)

在C中,可以(ab)使用预处理器自动生成2次幂枚举。如果有一个宏make_things扩展为“make_thing(flag1)make_thing(flag2)make_thing(flag3)”等,则可以多次调用该宏,并使用不同的make_thing定义,以实现2的幂国旗名称序列以及其他一些好东西。

例如,首先将make_thing(x)定义为“LINEAR_ENUM _ ## x”(包括逗号),然后使用enum语句生成枚举列表(包括在make_things宏之外的LINEAR_NUM_ITEMS)。然后创建另一个枚举,这次将make_thing(x)定义为“FLAG_ENUM _ ## x = 1&lt;

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