为什么人们总是使用枚举值如 0、1、2、4、8
,而不是 0、1、2、3、4
呢?
这是否与位运算有关?
我很感激如果能提供一个小的示例片段来展示如何正确使用它 :)
[Flags]
public enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
为什么人们总是使用枚举值如 0、1、2、4、8
,而不是 0、1、2、3、4
呢?
这是否与位运算有关?
我很感激如果能提供一个小的示例片段来展示如何正确使用它 :)
[Flags]
public enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
因为它们是2的幂,所以我可以这样做:
var permissions = Permissions.Read | Permissions.Write;
也许稍后再处理...
if( (permissions & Permissions.Write) == Permissions.Write )
{
// we have write access
}
这是一个位域(bit field),其中每个设置的位都对应于某个权限(或枚举值在逻辑上对应的内容)。如果将它们定义为1, 2, 3, ...
,你将无法以这种方式使用位运算符并获得有意义的结果。若想深入了解,请查看...
Permissions.Read == 1 == 00000001
Permissions.Write == 2 == 00000010
Permissions.Delete == 4 == 00000100
Notice a pattern here? Now if we take my original example, i.e.,var permissions = Permissions.Read | Permissions.Write;
那么...
permissions == 00000011
看到了吗?Read
和Write
位都被设置了,我可以独立检查它们(注意,Delete
位未设置,因此该值不表示删除权限)。
它允许将多个标志存储在一个位字段中。
myEnum.IsSet
。我认为这是一个完全无用的抽象,只是为了减少打字,但无所谓。 - Ed S.0
不等于false
;false
才是false
。但你可以这样写:if((permissions & Permissions.Write) > 0)
。 - Ed S.enum.HasFlag()
,而不是“棘手的” (permissions&Permissions.Write) == Permissions.Write
。 - Louis Kottmann如果其他回答仍不清晰,请这样考虑:
[Flags]
public enum Permissions
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
是写作更简洁的方式:
public enum Permissions
{
DeleteNoWriteNoReadNo = 0, // None
DeleteNoWriteNoReadYes = 1, // Read
DeleteNoWriteYesReadNo = 2, // Write
DeleteNoWriteYesReadYes = 3, // Read + Write
DeleteYesWriteNoReadNo = 4, // Delete
DeleteYesWriteNoReadYes = 5, // Read + Delete
DeleteYesWriteYesReadNo = 6, // Write + Delete
DeleteYesWriteYesReadYes = 7, // Read + Write + Delete
}
有八个可能性,但你可以将它们表示为仅四个成员的组合。如果有十六个可能性,那么可以将它们表示为仅五个成员的组合。如果有四十亿个可能性,那么可以将它们表示为仅三十三个成员的组合!显然,拥有仅33个成员(除零外每个成员都是2的幂),要比试图在枚举中命名四十亿个项要好得多。
2 ** -infinity
视为2的幂)。 - Brian1 == binary 00000001
2 == binary 00000010
4 == binary 00000100
等等,因此
1 | 2 == binary 00000011
EDIT:
3 == binary 00000011
在二进制中,3的值分别为1和2,它与1 | 2
的值相同。因此,当您尝试使用二进制位作为标志来表示某种状态时,通常情况下3并没有实际意义(除非有一个逻辑值实际上是这两个值的组合)。
为了进一步澄清,您可以将示例枚举扩展如下:
[Flags]
public Enum Permissions
{
None = 0, // Binary 0000000
Read = 1, // Binary 0000001
Write = 2, // Binary 0000010
Delete = 4, // Binary 0000100
All = 7, // Binary 0000111
}
Permissions.All
时,我也隐含拥有Permissions.Read
、Permissions.Write
和Permissions.Delete
。3
是二进制的11
,也就是说它不能被映射到一个单独的位上,所以你失去了将任意位置的1位映射到有意义的值的能力。 - Ed S.2|3 == 1|3 == 1|2 == 3
。因此,如果您有一个二进制值为 00000011
,并且您的标志包括值 1
、2
和 3
,那么您就不知道该值是表示 1 和 3
、2 和 3
、1 和 2
还是仅仅表示 3
。这使得它变得不那么有用。 - yshavit[Flags]
public Enum Permissions
{
None = 0; //0000000
Read = 1; //0000001
Write = 1<<1; //0000010
Delete = 1<<2; //0000100
Blah1 = 1<<3; //0001000
Blah2 = 1<<4; //0010000
}
我认为使用二进制移位运算符 <<
编写代码更易于理解和阅读,而且不需要进行计算。
这些用于表示位标志,允许组合枚举值。如果您使用十六进制符号来编写值,我认为会更清晰。
[Flags]
public Enum Permissions
{
None = 0x00,
Read = 0x01,
Write = 0x02,
Delete= 0x04,
Blah1 = 0x08,
Blah2 = 0x10
}
4194304
乘以2时会得到什么?那 0x400000
呢?识别 0x800000
作为正确答案比较容易,而且输入十六进制值也更少出错。 - phoog0x10000
是2的幂吗?是的,它以1、2、4或8开头,后面都是0。你不需要将0x10 心算成16(尽管这样做可能会变得自然),只需将其视为“某个2的幂”。请注意,不要改变原来的意思。 - Brian这其实更像是一条评论,但因为它不支持格式设置,我只想包含一个我用来设置标识枚举的方法:
[Flags]
public enum FlagTest
{
None = 0,
Read = 1,
Write = Read * 2,
Delete = Write * 2,
ReadWrite = Read|Write
}
我发现这种方法在开发过程中特别有帮助,尤其是当你想要按字母顺序维护你的标志时。如果你确定需要添加一个新的标志值,你只需要按字母顺序插入它,而唯一需要更改的值就是它现在前面的那个。
然而,请注意,一旦解决方案发布到任何生产系统(特别是如果枚举暴露在没有紧密耦合的情况下,例如通过 Web 服务),则强烈建议不要更改枚举中的任何现有值。
typedef NS_OPTIONS(NSUInteger, Align) {
AlignLeft = 00000001,
AlignRight = 00000010,
AlignTop = 00000100,
AlignBottom = 00001000,
AlignTopLeft = 00000101,
AlignTopRight = 00000110,
AlignBottomLeft = 00001001,
AlignBottomRight = 00001010
};
NSLog(@"%ld == %ld", AlignLeft | AlignBottom, AlignBottomLeft);
日志 513 == 513
这样更容易理解(至少对我来说)。将数字对齐…描述所需结果,得到想要的结果…没有必要进行“计算”。
|
(和&
)所代表的内容。各种回答都假设你已经熟悉它们。 - Brian