Flags enum&按位运算与“位串”

时间:2009-08-17 02:58:11

标签: c# bit-manipulation enum-flags

一位开发人员建议我们将一周中选择的日期存储为7个字符的1和0字符串,即周一和周五的“1000100”。我更喜欢(强烈建议)一个带有Flags枚举和按位操作的解决方案,我认为这是一种更简洁的方法,对其他开发人员应该更容易理解。

  [Flags()]
  public enum Weekdays : int
  {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64
  }

然而,当我开始实现一个示例解决方案时,我意识到简单的字符串方法可能更容易:当然,如果您只是查看数据,则位串比“17”更明显。而且我发现C#按位运算反直觉而且非常冗长:

Weekdays workDays = Weekdays.Monday | Weekdays.Tuesday;
if ((workDays & Weekdays.Monday) == Weekdays.Monday) 
{...}

当然这可以很好地包含在扩展方法中,但是我们突然最终得到的代码行数与字符串解决方案的数量相同,而且我很难说比特代码更容易阅读。

话虽如此,我仍然会使用标志枚举和按位操作。我能想到的主要好处是

  • 更好的表现
  • 存储所需的空间更少

那么我如何向同事出售按位解决方案呢?我是不是该?使用此方法比字符串有什么其他好处?完成示例项目后,我发现团队仍然选择了基于字符串的解决方案。我需要一些更好/更强的论点。 为什么要使用Flags枚举而不是简单的位串?

6 个答案:

答案 0 :(得分:41)

使用Flags枚举的好处:

使用标志枚举的否定:

  • 难以理解的人类数据表示(例如,为17设置了哪些标志?)


使用字符串的好处:

  • 程序员可以轻松查看字符串
  • 中设置的位数

使用字符串的否定:

  • 非标准方法
  • 更难理解不熟悉您设计的程序员
  • 可能更容易设置“垃圾”值(例如stringValue =“Sunday”)
  • 不必要的字符串创建
  • 不必要的字符串解析
  • 其他开发工作
  • 重新发明轮子(但不是圆轮)


能够查看比特串以查看所设置的内容真的有多重要?如果很难知道17是星期一和星期五,你可以随时使用计算器并转换为二进制。或者为“显示”(或调试)使用添加某种字符串表示。 并非


在我看来,如果你要使比特串变得坚实,那么你将需要进行相当多的封装,以使其达到Flags枚举的抽象级别。提供。如果方法是简单地直接操作位串,则难以阅读(并理解)并且可能容易出错。

e.g。你最终可能会看到这个:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"

答案 1 :(得分:23)

您不应该创建非标准数据结构来替换标准数据结构(在本例中为DayOfWeek内置枚举)。相反,扩展现有结构。这与您正在讨论的位标记方法的工作方式基本相同。

namespace ExtensionMethods
{
    public static class Extensions
    {
        /*
         * Since this is marked const, the actual calculation part will happen at
         * compile time rather than at runtime.  This gives you some code clarity
         * without a performance penalty.
         */
        private const uint weekdayBitMask =
            1 << Monday 
            | 1 << Tuesday
            | 1 << Wednesday
            | 1 << Thursday
            | 1 << Friday;
        public static bool isWeekday(this DayOfWeek dayOfWeek)
        {
            return 1 << dayOfWeek & weekdayBitMask > 0;
        }
    }   
}

现在您可以执行以下操作:

Thursday.isWeekday(); // true
Saturday.isWeekday(); // false

答案 2 :(得分:6)

创建一个可以保持工作日组合的课程。在类中,您可以以任何方式表示数据,但我肯定会使用标志枚举而不是字符串。在类之外,您只需使用枚举值,并将实际逻辑封装在类中。

类似的东西:

[Flags]
public enum Days {
   Monday = 1,
   Tuesday = 2,
   Wednesday = 4,
   Thursday = 8,
   Friday = 16,
   Saturday = 32,
   Sunday = 64,
   MondayToFriday = 31,
   All = 127,
   None = 0
}

public class Weekdays {

   private Days _days;

   public Weekdays(params Days[] daysInput) {
      _days = Days.None;
      foreach (Days d in daysInput) {
         _days |= d;
      }
   }

   public bool Contains(Days daysMask) {
      return (_days & daysMask) == daysMask;
   }

   public bool Contains(params Days[] daysMasks) {
      Days mask = Days.None;
      foreach (Days d in daysMasks) {
         mask |= d;
      }
      return (_days & mask) == mask;
   }

}

用法示例:

Weekdays workdays = new Weekdays(Days.MondayToFriday);
if (workdays.Contains(Days.Monday, Days.Wednesday)) {
   ...
}

答案 3 :(得分:1)

问题应该围绕人眼是否真正看到这个储值。如果是这样,一个有点人类可读的格式显然很重要(尽管如果是这样的话,我会为更大的东西做出争论,比如实际名称的数组)。

然而,至少在我构建的所有应用程序中,这种数据在某个地方进入一个小字段并且再也看不到了,除了通过c#代码 - 这意味着bitflags绝对是最简单的 - 它们是代码中最易读的 。你的同事真的想写一个将0和1映射到值的字符串解析器,而不是使用内置使用40多年按位操作的想法吗?

答案 4 :(得分:0)

有趣的是,这两种方法完全相同;只有flags方法更明显。

我个人会使用这些标志(尽管可能,根据您的模型,最好将列表作为列表存储,不管是谁持有它)。

- 编辑

很明显,我认为,表现真的不需要考虑你正在做的事情。所以只需要最具可读性。 (其中,恕我直言,是命名的旗帜)。

答案 5 :(得分:0)

Flags方法是惯用的(即它是有经验的程序员所做的,习惯于看到和做的,至少在C / C ++ / C#语言中)。