36

一位开发人员建议我们将一周中的选定日期存储为 1 和 0 的 7 个字符串,即星期一和星期五的“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) 
{...}

当然,这可以很好地包装到扩展方法中,但是我们突然之间会得到至少与字符串解决方案相同的代码行数,而且我很难说按位代码更容易阅读。

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

  • 更好的性能
  • 存储所需空间更少

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

4

6 回答 6

43

使用 Flags 枚举的好处:

使用 Flags 枚举的负面影响:

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


使用位串的好处:

  • 便于程序员查看字符串中设置了哪些位

使用位串的负面影响:

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


能够查看位串以了解设置的内容到底有多重要?如果很难知道 17 是星期一和星期五,您可以随时使用计算器并转换为二进制。或者添加某种字符串表示形式以供“显示”(或调试)使用。这并不


在我看来,如果您要使位串接近可靠,那么您将需要进行大量封装以将其提升到 Flags 枚举已经提供的抽象级别。如果方法是简单地直接操作比特串,那么这将难以阅读(和理解)并且可能容易出错。

例如,您最终可能会看到:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"
于 2009-08-17T06:42:25.267 回答
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
于 2009-08-17T06:38:53.933 回答
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)) {
   ...
}
于 2009-08-17T04:10:58.743 回答
1

问题应该集中在人眼是否会真正看到这个存储值。如果是这样,那么某种人类可读的格式显然很重要(尽管如果是这样的话,我会为更大的东西做一个论据,比如真实日期名称的数组)。

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

于 2009-08-17T03:02:00.793 回答
0

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

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

- 编辑

需要明确的是,我认为性能真的不需要考虑你在做什么。因此,请选择最易读的。(恕我直言,这是命名的标志)。

于 2009-08-17T03:00:45.270 回答
0

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

于 2009-08-17T03:12:41.760 回答