为啥枚举权限经常有 0、1、2、4 个值?
Posted
技术标签:
【中文标题】为啥枚举权限经常有 0、1、2、4 个值?【英文标题】:Why do enum permissions often have 0, 1, 2, 4 values?为什么枚举权限经常有 0、1、2、4 个值? 【发布时间】:2012-04-06 08:31:29 【问题描述】:为什么人们总是使用像0, 1, 2, 4, 8
这样的枚举值而不是0, 1, 2, 3, 4
?
这和位操作等有关系吗?
我非常感谢一个关于如何正确使用它的小样本 sn-p :)
[Flags]
public enum Permissions
None = 0,
Read = 1,
Write = 2,
Delete = 4
【问题讨论】:
Enum as Flag using, setting and shifting 的可能重复项 我不同意欺骗投票。 UNIX设置权限的方式也是基于同样的逻辑。 @Pascal:您可能会发现了解Bitwise OR(和Bitwise AND)会有所帮助,这就是|
(和&
)所代表的内容。各种答案假设您熟悉它。
@IAdapter 我明白你为什么会这样认为,因为两者的答案是相同的,但我认为问题是不同的。另一个问题只是要求提供 C# 中 Flags 属性的示例或解释。这个问题似乎是关于位标志的概念,以及它们背后的基本原理。
【参考方案1】:
如果从其他答案中仍然不清楚,请这样考虑:
[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
有八种可能性,但您可以将它们表示为只有四个成员的组合。如果有十六种可能性,那么您可以将它们表示为只有五个成员的组合。如果有 40 亿种可能性,那么您可以将它们表示为仅 33 个成员的组合!显然,只有 33 个成员,每个成员(除了零)是 2 的幂,比尝试在一个枚举中命名 40 亿个项目要好得多。
【讨论】:
+1 表示拥有 40 亿成员的enum
的心理形象。可悲的是,可能有人已经尝试过了。
@DanielPryden 作为 Daily WTF 的每日读者,我相信。
2^33 = ~86 亿。对于 40 亿个不同的值,您只需要 32 位。
@MichaelKjörling 33 个中的一个用于 0 默认
@MichaelKjörling:公平地说,只有 32 个成员是 2 的幂,因为 0 不是 2 的幂。所以“33 个成员,每个成员是 2 的幂”并不完全正确(除非你把 2 ** -infinity
算作 2 的幂)。【参考方案2】:
很多很好的答案...另一种选择(我敢说,简单的枚举声明风格)......
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
理解起来要容易得多(至少对我自己而言)。排列好……描述你想要的结果,得到你想要的结果。不需要“计算”。
【讨论】:
【参考方案3】:这些用于表示允许枚举值组合的位标志。我认为如果你用十六进制表示法写值会更清楚
[Flags]
public Enum Permissions
None = 0x00,
Read = 0x01,
Write = 0x02,
Delete= 0x04,
Blah1 = 0x08,
Blah2 = 0x10
【讨论】:
@Pascal:也许此时它对您来说更具可读性,但是随着您获得以十六进制查看字节的经验,这将成为第二天性。十六进制中的两位数字映射到一个字节映射到 8 位(嗯……一个字节通常是 8 位……并不总是如此,但对于这个例子,可以概括)。 @Pascal 快,将4194304
乘以 2 会得到什么? 0x400000
怎么样?将0x800000
识别为正确答案比8388608
更容易识别,而且输入十六进制值也不易出错。
如果您使用十六进制,一目了然地判断您的标志是否设置正确(即,是 2 的幂)要容易得多。 0x10000
是 2 的幂吗?是的,它以 1、2、4 或 8 开头,之后全为 0。您不需要在心里将 0x10 翻译成 16(尽管这样做最终可能会成为第二天性),只需将其视为“2 的某种幂”即可。
我完全同意 Jared 的看法,因为它更容易用十六进制记录。你只需使用 1 2 4 8 和 shift
就个人而言,我更喜欢只使用例如P_READ=1
【参考方案4】:
[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
我觉得这样写更容易理解和阅读,也不需要计算。
【讨论】:
【参考方案5】:这实际上更像是一个评论,但由于它不支持格式化,所以我只想包含一个我用来设置标志枚举的方法:
[Flags]
public enum FlagTest
None = 0,
Read = 1,
Write = Read * 2,
Delete = Write * 2,
ReadWrite = Read|Write
如果您喜欢按字母顺序维护标志,我发现这种方法在开发过程中特别有用。如果您确定需要添加一个新的标志值,您可以按字母顺序插入它,您唯一需要更改的值就是它现在之前的那个值。
但是请注意,一旦将解决方案发布到任何生产系统(特别是如果枚举在没有紧密耦合的情况下公开,例如通过 Web 服务),那么强烈建议不要更改枚举中的任何现有值.
【讨论】:
【参考方案6】:因为这些值代表二进制中唯一的位位置:
1 == binary 00000001
2 == binary 00000010
4 == binary 00000100
等等,所以
1 | 2 == binary 00000011
编辑:
3 == binary 00000011
二进制中的 3 在个位和二进制位都由值 1 表示。它实际上与值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
【讨论】:
那么 2|3 有什么问题? @Pascal:因为3
是11
二进制,即它不映射到单个设置位,因此您无法将任意位置的1 位映射到有意义的值.
@Pascal 换句话说,2|3 == 1|3 == 1|2 == 3
。因此,如果您有一个二进制值00000011
,并且您的标志包含值1
、2
和3
,那么您将不知道该值是否代表1 and 3
、2 and 3
、@987654339 @ 或 only 3
。这使得它的用处大大降低。【参考方案7】:
因为它们是二的幂,我可以这样做:
var permissions = Permissions.Read | Permissions.Write;
也许以后...
if( (permissions & Permissions.Write) == Permissions.Write )
// we have write access
它是一个位域,其中每个设置位对应于某个权限(或任何枚举值逻辑上对应的)。如果这些被定义为1, 2, 3, ...
,您将无法以这种方式使用位运算符并获得有意义的结果。深入研究...
Permissions.Read == 1 == 00000001
Permissions.Write == 2 == 00000010
Permissions.Delete == 4 == 00000100
注意到这里的模式了吗?现在,如果我们以我原来的例子为例,即
var permissions = Permissions.Read | Permissions.Write;
那么……
permissions == 00000011
看到了吗? Read
和 Write
位都已设置,我可以独立检查(另请注意,Delete
位 not 已设置,因此该值不传达删除权限)。
它允许在单个位字段中存储多个标志。
【讨论】:
@Malcolm:确实如此;myEnum.IsSet
。我认为这是一个完全没用的抽象,只是为了减少打字,但是嗯
很好的答案,但您应该提及为什么应用 Flags 属性,以及何时不想将 Flags 应用于某些枚举。
@Andy:实际上,Flags
属性只是为您提供“漂亮的打印”iirc。无论属性是否存在,您都可以使用枚举值作为标志。
@detly:因为 C# 中的 if 语句需要布尔表达式。 0
不是 false
; false
是 false
。但是你可以写if((permissions & Permissions.Write) > 0)
。
现在可以使用(permissions & Permissions.Write) == Permissions.Write
,而不是“棘手的”enum.HasFlag()
以上是关于为啥枚举权限经常有 0、1、2、4 个值?的主要内容,如果未能解决你的问题,请参考以下文章
调试为啥我在 Apache 2.4 中得到“你没有访问权限”