【问题标题】: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
}

【问题讨论】:

  • 我不同意欺骗投票。
  • UNIX设置权限的方式也是基于同样的逻辑。
  • @Pascal:您可能会发现了解Bitwise OR(和Bitwise AND)会有所帮助,这就是|(和&)所代表的内容。各种答案假设您熟悉它。
  • @IAdapter 我明白你为什么会这样认为,因为两者的答案是相同的,但我认为问题是不同的。另一个问题只是要求提供 C# 中 Flags 属性的示例或解释。这个问题似乎是关于位标志的概念,以及它们背后的基本原理。

标签: c# permissions enums 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()
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-06-21
        • 1970-01-01
        • 1970-01-01
        • 2011-12-11
        • 1970-01-01
        • 2022-01-11
        • 1970-01-01
        相关资源
        最近更新 更多