为啥枚举权限经常有 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.ReadPermissions.WritePermissions.Delete

【讨论】:

那么 2|3 有什么问题? @Pascal:因为311 二进制,即它不映射到单个设置位,因此您无法将任意位置的1 位映射到有意义的值. @Pascal 换句话说,2|3 == 1|3 == 1|2 == 3。因此,如果您有一个二进制值00000011,并且您的标志包含值123,那么您将不知道该值是否代表1 and 32 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

看到了吗? ReadWrite 位都已设置,我可以独立检查(另请注意,Deletenot 已设置,因此该值不传达删除权限)。

它允许在单个位字段中存储多个标志。

【讨论】:

@Malcolm:确实如此; myEnum.IsSet。我认为这是一个完全没用的抽象,只是为了减少打字,但是嗯 很好的答案,但您应该提及为什么应用 Flags 属性,以及何时不想将 Flags 应用于某些枚举。 @Andy:实际上,Flags 属性只是为您提供“漂亮的打印”iirc。无论属性是否存在,您都可以使用枚举值作为标志。 @detly:因为 C# 中的 if 语句需要布尔表达式。 0 不是 falsefalsefalse。但是你可以写if((permissions &amp; Permissions.Write) &gt; 0) 现在可以使用(permissions &amp; Permissions.Write) == Permissions.Write,而不是“棘手的”enum.HasFlag()

以上是关于为啥枚举权限经常有 0、1、2、4 个值?的主要内容,如果未能解决你的问题,请参考以下文章

调试为啥我在 Apache 2.4 中得到“你没有访问权限”

UVa 1152 和为0的4个值(二分查找)

为啥我回答问题,上传图片成功,却出现权限不足,不能上传是怎么回事啊?我现在是四级。

使用root为啥还提示权限不够

文件权限

分享:Android系统的经常使用权限整理