【问题标题】:Testing for bitwise Enum values测试按位枚举值
【发布时间】:2011-05-24 04:21:09
【问题描述】:

我以前没有真正使用过按位枚举,我只是想确保我的测试是正确的。我最感兴趣的是测试 None 和 All 的值。我们从 Web 服务接收数据,该服务利用此枚举对某些数据进行分类。鉴于此,我假设 None 或 All 都不会与任何其他值结合使用。

给定以下按位枚举定义;

[System.FlagsAttribute()]
public enum TrainingComponentTypes : int
    {
        None = 0,
        AccreditedCourse = 1,
        Qualification = 2,
        Unit = 4,
        SkillSet = 8,
        UnitContextualisation = 16,
        TrainingPackage = 32,
        AccreditedCourseModule = 64,
        All = 127,
    }

我在this MSDN site 上阅读了以下关于 FlagAttributes 的引述;

使用 None 作为标志的名称 枚举常量,其值为 零。您不能使用无 按位与枚举常量 测试标志的操作,因为 结果始终为零。然而, 你可以执行一个逻辑,而不是 按位比较 数值和 None 枚举 常量来确定是否有任何位 在数值中设置。

在这种情况下,逻辑比较是否指的是枚举的正常相等测试? 例如;

TrainingComponentTypes tct = TrainingComponentTypes.None; 
if (tct == TrainingComponentTypes.None) 
{ ... }

对于按位比较,我正在执行以下操作;

 TrainingComponentTypes tct = TrainingComponentTypes.AccreditedCourse | TrainingComponentTypes.Qualification | TrainingComponentTypes.TrainingPackage;
 Assert.IsTrue((tct & TrainingComponentTypes.AccreditedCourse) == TrainingComponentTypes.AccreditedCourse, "Expected AccreditedCourse as part the enum");

 Assert.IsFalse((tct & TrainingComponentTypes.SkillSet) == TrainingComponentTypes.SkillSet, "Found unexpected SkillSet as part the enum");

最后,在对所有人进行测试时,我尝试了逻辑比较和按位比较,它们都返回相同的结果。我应该在这里使用另一个吗?例如;

TrainingComponentTypes tct = TrainingComponentTypes.All;

Assert.IsTrue((tct & TrainingComponentTypes.All) == TrainingComponentTypes.All, "Expected All as part the enum");
Assert.IsTrue((tct) == TrainingComponentTypes.All, "Expected All as part the enum");
// The follow also pass the assertion for a value of All
Assert.IsTrue((tct & TrainingComponentTypes.Qualification) == TrainingComponentTypes.Qualification, "Expected Qualification as part the enum");
Assert.IsTrue((tct & TrainingComponentTypes.TrainingPackage) == TrainingComponentTypes.TrainingPackage, "Expected TrainingPackage as part the enum");

总之,我想了解以下有关位枚举的信息;

  1. 是我对逻辑的理解 给出我的例子比较正确 多于?
  2. 是我的表现方式 按位比较正确吗?
  3. 处理“全部”的正确方法是什么 值(按位或逻辑)。我不确定我们是否会收到 All 与其他 TrainingComponentTypes 组合的值。我不明白为什么我们会,但是,你永远不知道吗?
  4. 我假设那个开关是对的吗 声明基本上不应该 用于按位枚举(无 似乎是一种特殊情况,并且 需要逻辑比较)?

谢谢, 克里斯

【问题讨论】:

    标签: c# enums comparison bit-manipulation bit


    【解决方案1】:

    简短回答:是的:)

    更长:

    1) 所有的操作都是在flags变量的整数值上进行的,所以你可以从这个角度来考虑。

    2) 是的。

    3) 两者都可以。但是,值得注意的是,如果有人将无效值塞入变量中,那么== TrainingComponentTypes.All 版本将失败。例如:

    var badValue = (TrainingComponentTypes)128 | TrainingComponentTypes.All;
    // now badValue != TrainingComponentTypes.All
    // but (badValue & TrainingComponentTypes.All) == TrainingComponentTypes.All
    

    对于这部分:

    我不确定我们是否会收到 All 与其他 TrainingComponentTypes 组合的值。

    我不确定你是否完全理解枚举是如何在幕后工作的。

    The value of All is:
        127 = 1111111 (binary)
    
    The other values are:
        AccreditedCourse       = 0000001
        Qualification          = 0000010
        Unit                   = 0000100
        SkillSet               = 0001000
        UnitContextualisation  = 0010000
        TrainingPackage        = 0100000
        AccreditedCourseModule = 1000000
    

    如您所见,All 只是所有这些值的按位|。您不能将任何其他 TraningComponentTypes 与 All 组合,因为 All 已经包含它们!此外,如果您将它们与| 自己组合在一起,则与直接使用 All 完全相同(因此,当您在枚举中定义 All 时,它只是一种方便)。

    4) 您可以使用它来检查 None 或 All 但不能检查其他值。

    值得注意的是,在 Enum 上有一个方便的方法可以为您执行这些检查:Enum.HasFlag

    【讨论】:

    • 感谢您的详细解答。这个问题的大多数答案都提供了非常相似的解释,但你的答案是完整的详细解释。
    【解决方案2】:

    鉴于我上面的例子,我对逻辑比较的理解是否正确?

    是的,在这种情况下,逻辑意味着相等和不等运算符。

    我执行按位比较的方式是否正确?

    是的,但有一个更简单的方法:Enum.HasFlag。例如:

    tct.HasFlag(TrainingComponentTypes.Qualification)
    

    代替:

    (tct & TrainingComponentTypes.Qualification) == TrainingComponentTypes.Qualification
    

    处理“All”值(按位或逻辑)的正确方法是什么。我不确定我们是否会收到 All 与其他 TrainingComponentTypes 组合的值。我不明白我们为什么会这样做,但是,你永远不知道吗?

    我认为最好在enum 本身中将All 定义为其所有部分的按位或。但你会看到人们会同时做这件事。

    我是否正确假设 switch 语句基本上不应该用于按位枚举(假设没有似乎是一种特殊情况,需要进行逻辑比较)?

    不,一点也不。随意使用它们是switch 声明。 case 值必须是常量,但它们可以是表达式并经过相等性测试。如果你做了一些愚蠢的事情,比如尝试使用相同的 case 值两次,编译器会告诉你。

    【讨论】:

    • 感谢您提供详细信息。我正在为这个特定项目使用 .NET 3.5,我不相信我可以在这个版本中使用 Enum.HasFlag。文档似乎显示它适用于 .NET 4.0,但之前没有。
    • 我听说有人说使用Enum.HasFlag 不是检查位值的最佳方法,但我经常不同意。任何人都可以证明为什么 Enum.HasFlag 可能无法像手动检查那样工作吗?
    • Enum.HasFlag 在第一次添加时非常缓慢,但后来得到了改进。另见answer
    • 这 - 好多了。
    【解决方案3】:
    1. 是的。

    2. 逻辑和位都可以使用。用法取决于是设置所有位还是仅设置您定义的所有值的按位或。

    3. 是的,但不是因为None。开关比较单个值,而位域显然可以有多个值。

    正如其他人所指出的,Enum 包含 HasFlag()

    【讨论】:

    • -“使用取决于是所有位都设置了还是只是您定义的所有值的按位或”- 太好了。感谢您的信息。
    【解决方案4】:

    1 和 2 - 是的,但是有一种方法可以使它更易于阅读:

    TrainingComponentTypes tct = TrainingComponentTypes.AccreditedCourse | TrainingComponentTypes.Qualification;
    Assert.IsTrue(tct.HasFlag(TrainingComponentTypes.AccreditedCourse), "Expected AccreditedCourse as part the enum");
    

    3 - 我不确定你是否需要 All 值。我会删除它。

    4 - 是的,switch 语句通常对 Flags 枚举没有意义。

    【讨论】:

      【解决方案5】:

      “3.处理“All”值(按位或逻辑)的正确方法是什么。我不确定我们是否会收到 All 与其他 TrainingComponentTypes 组合的值。我不明白为什么我们会,但是“

      您似乎误解了按位枚举值的工作原理。 'All' 总是与其他值组合,实际上它是所有值的组合。查看枚举的二进制值:

      无 = 0,
      认可课程 = 1,
      资格 = 10,
      单位 = 100,
      技能组 = 1000,
      UnitContextualisation = 10000,
      培训包 = 100000,
      AccreditedCourseModule = 1000000,
      全部 = 1111111

      这对你的理解有帮助吗?

      【讨论】:

      • 是的。谢谢。我以前没有使用过按位枚举,所以这对我来说有点新。带我回到大学一年级,但那是不久前的事了。
      【解决方案6】:

      1&2 看起来不错

      3。 您定义的所有内容都不能与任何内容结合使用而不会丢失数据。如果“all”是您希望从服务器接收到的真实值,您可能应该将其更改为 128。

      否则它是一个方便的值,您可以使用它来测试是否设置了任何值...除非您的标志值作为二进制数据发送并打包在一个可能包含其他数据的字节中,否则您不需要它。

      4。 可以使用 switch 语句,但如果/当您的值具有多个标志时,如果存在有效标志组合的小子集,它们仍然有用,则它们将无法正常工作。

      【讨论】:

        【解决方案7】:

        在模型中 as_enum :shifts, { day_shift: 0, 晚间班次:1, 夜班:2, }, 访问器: :bitwise

        在视图中 Model.enum_collection(:shifts)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-11-15
          • 1970-01-01
          • 2012-06-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多