标志枚举 &按位运算与“位串”

发布于 2024-08-02 05:28:37 字数 852 浏览 2 评论 0原文

一位开发人员同事建议我们将一周中的几天选择存储为由 1 和 0 组成的 7 个字符的字符串,即“1000100”代表周一和周五。我更喜欢(并强烈建议)带有标志枚举和按位运算的解决方案,我认为这是一种更简洁的方法,并且对于其他开发人员来说应该更容易理解。

  [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 枚举而不是简单的位串?

A fellow developer suggested we store a selection of days of the week as 7-character string of 1’s and 0’s, i.e. “1000100” for Monday and Friday. I preferred (and strongly suggested) a solution with a Flags enum and bitwise operations, I think it's a cleaner way of doing this, and it should be easier to understand for other developers.

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

However, as I started to implement a sample solution, I realized that maybe the simple string approach was easier after all: Certainly the bit-string is more obvious than “17” if you’re just looking at the data. And I find the C# bitwise operations counter-intuitive and extremely verbose:

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

Of course this could be wrapped nicely into extension methods, but then we suddenly end up with at least the same number of lines of code as with the string-solution, and I can hardly argue the bitwise code is easier to read.

That being said, I still would go with a flags enum and bitwise operations. The key benefits I can think of are

  • Better performance
  • Less space needed for storage

So how do I sell the bitwise solution to my colleagues? Should I? What are the other benefits of using this method over strings? After completing the sample project, I found that the team still opted for the string-based solution. I need some better/stronger arguments.
Why should you use Flags enums rather than simple bit-strings?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

段念尘 2024-08-09 05:28:37

使用 Flags 枚举的好处:

< strong>使用 Flags 枚举的缺点:

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

使用位串的好处:

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

使用位串的缺点:

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

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

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

例如你可能最终会看到这个:

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

Benefits of using Flags enum:

Negatives of using Flags enum:

  • Data representation for humans hard to understand (e.g. what flags are set for 17?)



Benefits of using string of bits:

  • Easy for programmers to see which bits are set in string

Negatives of using string of bits:

  • Non-standard approach
  • Harder to understand for programmers unfamiliar with your design
  • Potentially easier to set "garbage" values (e.g. stringValue = "Sunday")
  • Needless string creation
  • Needless string parsing
  • Additional development work
  • Reinventing the wheel (but not even a round wheel)



How important is it really to be able to look at the string of bits to see what is set? If it's hard to know that 17 is Monday and Friday, you can always use calculator and convert to binary. Or add some sort of string representation for "display" (or debugging) use. It's not that difficult.


It also seems to me that if you are going to make the string of bits approach solid then you will need to do quite a bit of encapsulation to bring it up to a level of abstraction that the Flags enum already provides. If the approach is to simply manipulate the string of bits directly then that is going to be hard to read (and understand) and probably error prone.

e.g. you may end up seeing this:

days = "1000101"; // fixed bug where days were incorrectly set to "1010001"
对风讲故事 2024-08-09 05:28:37

您不应该创建非标准数据结构来替换标准数据结构(在本例中为 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

You shouldn't be creating non-standard datastructures to replace a standard data structure (in this case, the DayOfWeek builtin enum). Instead, extend the existing structure. This works essentially the same way as the bit flags method you were talking about.

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;
        }
    }   
}

Now you can do the following:

Thursday.isWeekday(); // true
Saturday.isWeekday(); // false
萌辣 2024-08-09 05:28:37

做一个能容纳平日组合的班级。在类中,您可以以任何一种方式表示数据,但我肯定会选择标志枚举而不是字符串。在类之外,您只需使用枚举值,实际逻辑封装在类中。

类似于:

[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)) {
   ...
}

Make a class that can hold the combination of weekdays. Inside the class you can represent the data either way, but I would definitely go for a flags enumeration rather than a string. Outside the class you just use the enum values, and the actual logic is encapsulated in the class.

Something like:

[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;
   }

}

Usage example:

Weekdays workdays = new Weekdays(Days.MondayToFriday);
if (workdays.Contains(Days.Monday, Days.Wednesday)) {
   ...
}
那请放手 2024-08-09 05:28:37

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

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

The question should center around whether human eyes will ever actually see this stored value. If so, a somewhat human-readable format is obviously important (though if that is the case, I'd make an argument for something even larger, like an array of the real day names).

However, at least in all the apps I've ever built, this kind of data goes into a tiny field somewhere and is never seen again, except via the c# code - which means bitflags are definitely the simplest - they are the most human-readable in code. Your colleagues really want to write a string parser that maps 0's and 1's to values instead of using the built in and used for 40+ years idea of bitwise operations?

新人笑 2024-08-09 05:28:37

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

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

-- 编辑

需要明确的是,我认为,性能确实不需要成为您正在做的事情的考虑因素。所以就选择最具可读性的。 (恕我直言,这是命名标志)。

Amusingly both of those methods are exactly the same; only the flags method is more obvious.

I'd personally go with the flags (though potentially, depending on your model, it'd be better just to store the list as an List against whoever is holding it).

-- Edit

And to be clear, performance really doesn't need to be a consideration for what you're doing, I think. So just go with the most readable. (Which, IMHO, is the named flags).

陈独秀 2024-08-09 05:28:37

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

The Flags method is idiomatic (i.e. it's what experienced programmers do and are accustomed to seeing and doing, at least in C/C++/C# languages).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文