views:

1025

answers:

6

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?

A: 

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
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
It's critical they learn bitwise operations anyway. I'd use this very simple case as a way to teach them, personally.
Noon Silk
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
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<DayOfWeek> 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
+1  A: 

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
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
@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
"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
A: 

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
I totally agree: I just need that killer-argument to turn them over.
Jakob Gade
+2  A: 

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
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
@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
+5  A: 

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
Simple, Clever. I like the extension approach. Extensions for Enumerations are powerful.
AMissico
+10  A: 
Tuzo
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
You have two "Negatives of using Flags enum", the second should be "Negatives of using string of bits@
Alexey Romanov
@Alexey: Thanks...fixed now.
Tuzo