C# 旗帜枚举及;逐位运算与&x201C;位串”;
一位同事建议我们将一周中选择的几天存储为1和0的7个字符字符串,即周一和周五的“1000100”。我更喜欢(并强烈建议)带有标志枚举和位操作的解决方案,我认为这是一种更干净的方法,其他开发人员应该更容易理解C# 旗帜枚举及;逐位运算与&x201C;位串”;,c#,bit-manipulation,enum-flags,C#,Bit Manipulation,Enum Flags,一位同事建议我们将一周中选择的几天存储为1和0的7个字符字符串,即周一和周五的“1000100”。我更喜欢(并强烈建议)带有标志枚举和位操作的解决方案,我认为这是一种更干净的方法,其他开发人员应该更容易理解 [Flags()] public enum Weekdays : int { Monday = 1, Tuesday = 2, Wednesday = 4, Thursday = 8, Friday = 16, Saturday =
[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方法更明显 我个人会使用这些标志(尽管可能,根据您的型号,最好将列表存储为一个列表,与持有它的人相对应) --编辑
我想,要明确的是,绩效真的不需要考虑你在做什么。所以就用最具可读性的。(其中,IMHO是命名的标志)。问题应该围绕人眼是否会真正看到这个存储值。若真是这样,一种人类可读的格式显然很重要(但若真是这样,我会提出更大的理由,比如一组真实的日名)
然而,至少在我构建的所有应用程序中,这种数据进入某个地方的一个小字段,再也看不到,除非通过c代码——这意味着位标志绝对是最简单的——它们是代码中最容易被人读取的。您的同事真的想编写一个字符串解析器,将0和1映射到值,而不是使用内置的、使用了40多年的按位操作思想?Flags方法是惯用的(即,这是有经验的程序员所做的,并且习惯于看到和做的,至少在C/C++/C语言中是如此).制作一个可以容纳工作日组合的类。在类中,您可以用任何一种方式表示数据,但我肯定会选择标志枚举而不是字符串。在类之外,您只需使用枚举值,实际逻辑封装在类中 比如:
[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)) {
...
}
您不应该创建非标准数据结构来替换标准数据结构(在本例中,是DayOfWeek内置的enum)。相反,扩展现有结构。这与您刚才提到的位标志方法的工作方式基本相同
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;
}
}
}
使用标志枚举的好处:
- 标准方法:
- 意图是明确的
- 可维护性——新程序员应该很容易学会这一点
- 易于扩展--支持新的旗帜组合(例如周末)
- 快速
- 难以理解的人的数据表示(例如,为17设置了哪些标志?)
使用位串的好处:
- 程序员很容易看到哪些位是以字符串形式设置的
- 非标准方法
- 对于不熟悉您的设计的程序员来说,更难理解
- 可能更容易设置“垃圾”值(例如stringValue=“Sunday”)
- 不必要的字符串创建
- 不必要的字符串解析
- 额外开发工作
- 重塑车轮(但甚至不是圆形车轮)
能够查看位串以查看设置的内容有多重要?如果很难知道17是星期一和星期五,您可以使用计算器将其转换为二进制。或者添加某种字符串表示法以供“显示”(或调试)使用。没那么难
在我看来,如果要使位字符串接近实数,则需要进行大量的封装,以使其达到标志枚举已经提供的抽象级别。如果方法是直接操作位串,那么这将很难读取(和理解),并且可能容易出错 e、 g.您可能最终会看到:
days = "1000101"; // fixed bug where days were incorrectly set to "1010001"
没有解决您的问题,但既然已经有了System.DayOfWeek,为什么还要为此创建自己的枚举?System.DayOfWeek枚举值是0到6,而不是2(1、2、4、8、16、32和64)的倍数,我需要执行位运算(AFAIK)。此外,星期一是一周的第一天为了减少冗长,您可以编写:
if((workDays&Weekdays.Monday)!=0)
让我建议第三个选项:HashSet
。您可以获得类型安全性和直观操作!我认为workDays.Contains(DayOfWeek.Monday)
比(workDays&Weekdays.Monday)!=0
或工作日[0]=“1”
。我不认为建议实际使用字符串作为“替代”方法,是吗?因为我同意这是疯狂的行为。也许我理解错了,我就是这么读的
days = "1000101"; // fixed bug where days were incorrectly set to "1010001"