36

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?

Eric
  • 95,302
  • 53
  • 242
  • 374
Jakob Gade
  • 12,319
  • 15
  • 70
  • 118
  • 4
    Doesn't address your question, but why create your own enum for this when there is already `System.DayOfWeek`? – Scott Dorman Aug 17 '09 at 03:02
  • 9
    The System.DayOfWeek enums values are 0 to 6, not multiples of 2 (1, 2, 4, 8, 16, 32 and 64) which I need to do the bitwise operations (AFAIK). Besides, MONDAY is the first day of the week. :) – Jakob Gade Aug 17 '09 at 03:30
  • To reduce verbose, you can write: `if ((workDays & Weekdays.Monday) != 0)` – Cœur Jun 17 '14 at 14:47
  • 1
    Let me suggest a third option: `HashSet`. You get both type safety *and* intuitive operations! I would argue that `workDays.Contains(DayOfWeek.Monday)` is more readable than *either* `(workDays & Weekdays.Monday) != 0` *or* `workDays[0] = '1'`. – Heinzi Mar 31 '16 at 14:26

6 Answers6

43

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"
Randy Levy
  • 22,566
  • 4
  • 68
  • 94
  • 2
    You can get the best of both worlds by using 0x0001 for Sunday, 0x0010 for Monday, 0x0100 for Tuesday, etc. This way when you convert the value to hex you get a human readable list of days, while retaining the advantages of using Flags. – Stefan Rusek Aug 17 '09 at 10:00
  • You have two "Negatives of using Flags enum", the second should be "Negatives of using string of bits@ – Alexey Romanov Aug 17 '09 at 12:14
  • 1
    You forgot the most critical negative of Flags enum: "Requires a recompile under normal circumstances" (and consequently a deployment) - which imho, would be the only negative worth considering in this design approach comparison. I only say this because your detailed, well placed answer, is probably garnering attention from coders not hurdling solely the proposed question from the OP... ;) – Dave Jellison Nov 05 '13 at 06:45
23

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
Imagist
  • 18,086
  • 12
  • 58
  • 77
6

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)) {
   ...
}
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • I think you misinterpreted the question... The question was not "How can I implement this using enums?" The question was more like "Which arguments can I use to convince my coworkers that an enum-based implementation is most appropriate?" – Alderath Aug 17 '09 at 07:15
  • 1
    @Alderath: Nope. My answer is just that the implementation should be encapsulated in a class so that it doesn't really matter how the values are represented. – Guffa Aug 17 '09 at 08:34
  • @Guffa Thanks for the compact example. – zionpi May 13 '14 at 08:10
1

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?

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • I don't think the suggestion was to actually use a *string* for the 'alternate' approach was it? Because that's just madness I agree. Maybe I mis-interpreted. – Noon Silk Aug 17 '09 at 03:06
  • @silky that's how I read it: "as 7-character string of 1’s and 0’s, i.e. “1000100” for Monday and Friday." crazy indeed – Rex M Aug 17 '09 at 03:07
  • "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?" ... sad and honest answer? Yes, that's the madness I witnessing. – Jakob Gade Aug 17 '09 at 03:31
0

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).

Noon Silk
  • 54,084
  • 6
  • 88
  • 105
  • I agree, performance is less important (although it may be an issue here, considering the approach they're opting for). I also think it's more readable, at least when you use a set of tiny extension methods for the bitwise operations (which, aparently, is too much for them to grok). – Jakob Gade Aug 17 '09 at 03:36
  • It's critical they learn bitwise operations anyway. I'd use this very simple case as a way to teach them, personally. – Noon Silk Aug 17 '09 at 03:37
  • Yup, hence the very simple sample application - which I hoped would teach them this. I need to include a couple of super-solid arguments with that sample... – Jakob Gade Aug 17 '09 at 03:46
  • It's hard to put into words how ridiculous and wrong it is to use a string of "0's" and "1's" to represent this structure. It boggles the mind. To be honest I'd prefer a List approach. But second to that, the flags method works. IMHO a 'strong' argument is not required; there is simply no allowance for the string model. I would not be working with someone who demanded an explanation of why (only to suggest they re-consider a career in programming). – Noon Silk Aug 17 '09 at 03:51
  • 1
    @NoonSilk that it's a bit harsh , I would't like to work with someone with such a arrogant attitude! – IronHide Jul 07 '16 at 12:39
0

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).

ChrisW
  • 54,973
  • 13
  • 116
  • 224