标志枚举和按位操作与"字符串"

Jak*_*ade 35 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
  }
Run Code Online (Sandbox Code Playgroud)

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

Weekdays workDays = Weekdays.Monday | Weekdays.Tuesday;
if ((workDays & Weekdays.Monday) == Weekdays.Monday) 
{...}
Run Code Online (Sandbox Code Playgroud)

当然,这可以很好地包含在扩展方法中,但是我们突然最终得到的代码行数与字符串解决方案的数量相同,我几乎不能认为按位代码更容易阅读.

话虽这么说,我仍然会使用标志枚举和按位操作.我能想到的主要好处是

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

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

Ran*_*ica 41

使用Flags枚举的好处:

使用Flags枚举的否定:

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


使用位串的好处:

  • 程序员可以轻松查看字符串中的哪些位

使用字符串的否定:

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


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


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

你最终可能会看到这个:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"
Run Code Online (Sandbox Code Playgroud)

  • 你可以通过周日使用0x0001,周一使用0x0010,周二使用0x0100等来获得两全其美.这样当你将值转换为十六进制时,你会得到一个人类可读的天数列表,同时保留使用Flags的优点. (2认同)

Ima*_*ist 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;
        }
    }   
}
Run Code Online (Sandbox Code Playgroud)

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

Thursday.isWeekday(); // true
Saturday.isWeekday(); // false
Run Code Online (Sandbox Code Playgroud)


Guf*_*ffa 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;
   }

}
Run Code Online (Sandbox Code Playgroud)

用法示例:

Weekdays workdays = new Weekdays(Days.MondayToFriday);
if (workdays.Contains(Days.Monday, Days.Wednesday)) {
   ...
}
Run Code Online (Sandbox Code Playgroud)