Determining if enum value is in list (C#)

14,674

Solution 1

Your current code will say whether it's exactly "raining and thundery". To find out whether it's "raining and thundery and possibly something else" you need:

if ((currentWeather.Type & _badWeatherTypes) == _badWeatherTypes)

To find out whether it's "raining or thundery, and possibly something else" you need:

if ((currentWeather.Type & _badWeatherTypes) != 0)

EDIT (for completeness):

It would be good to use the FlagsAttribute, i.e. decorate the type with [Flags]. This is not necessary for the sake of this bitwise logic, but affects how ToString() behaves. The C# compiler ignores this attribute (at least at the moment; the C# 3.0 spec doesn't mention it) but it's generally a good idea for enums which are effectively flags, and it documents the intended use of the type. At the same time, the convention is that when you use flags, you pluralise the enum name - so you'd change it to WeatherTypes (because any actual value is effectively 0 or more weather types).

It would also be worth thinking about what "Sunny" really means. It's currently got a value of 0, which means it's the absence of everything else; you couldn't have it sunny and raining at the same time (which is physically possible, of course). Please don't write code to prohibit rainbows! ;) On the other hand, if in your real use case you genuinely want a value which means "the absence of all other values" then you're fine.

Solution 2

I'm not sure that it should be a flag - I think that you should have an range input for:

  • Temperature
  • How much it's raining
  • Wind strength
  • any other input you fancy (e.g. thunderstorm)

you can then use an algorithm to determine if the conditions are sufficiently good.

I think you should also have an input for how likely the weather is to remain the same for cycling home. The criteria may be different - you can shower and change more easliy when you get home.

If you really want to make it interesting, collect the input data from a weather service api, and evaulate the decision each day - Yes, I should have cycled, or no, it was a mistake. Then perhaps you can have the app learn to make better decisions.

Next step is to "socialize" your decision, and see whether other people hear you are making the same decisions.

Solution 3

use the FlagsAttribute. That will allow you to use the enum as a bit mask.

Share:
14,674
Nathan Koop
Author by

Nathan Koop

I'm currently a Sr. Dev/tech lead at Bold Commerce. I work with PHP, Laravel, Eloquent, React, Redux, GoLang, MySQL and more.

Updated on July 29, 2022

Comments

  • Nathan Koop
    Nathan Koop almost 2 years

    I am building a fun little app to determine if I should bike to work.

    I would like to test to see if it is either Raining or Thunderstorm(ing).

    public enum WeatherType : byte
    { Sunny = 0, Cloudy = 1, Thunderstorm = 2, Raining = 4, Snowing = 8, MostlyCloudy = 16 }
    

    I was thinking I could do something like:

    WeatherType _badWeatherTypes = WeatherType.Thunderstorm | WeatherType.Raining;
    if(currentWeather.Type == _badWeatherTypes)
    {
     return false;//don't bike
    }
    

    but this doesn't work because _badWeatherTypes is a combination of both types. I would like to keep them separated out because this is supposed to be a learning experience and having it separate may be useful in other situations (IE, Invoice not paid reason's etc...).

    I would also rather not do: (this would remove the ability to be configured for multiple people)

    if(WeatherType.Thunderstorm)
    {
     return false; //don't bike
    }
    etc...
    
  • Jon Skeet
    Jon Skeet over 15 years
    You can use the enum as a bitmask without flags; FlagsAttribute just changes how ToString operates, I believe. It would still be a good idea to include it, admittedly.
  • Jon Skeet
    Jon Skeet over 15 years
    Please see my response to MagicKat. Flags is desirable but not necessary here.
  • Scott Dorman
    Scott Dorman over 15 years
    It does just change how ToString behaves, but there is no guarantee that future versions of .NET won't change other behaviors (like the compiler issuing errors if the values aren't powers of 2) so you should always use it when using a flags enum.
  • MagicKat
    MagicKat over 15 years
    Ah, I didn't know that it just changed the ToString(). Interesting.
  • Mark Brackett
    Mark Brackett over 15 years
    If you don't specify the values, Flag will set them appropiately to powers of 2 (at least in VB).
  • Jon Skeet
    Jon Skeet over 15 years
    Flags doesn't change the autogenerated values in C#, unfortunately. As for "no guarantee that future versions of .NET won't change other behaviours" - that's like saying "there's no guarantee that the garbage collector will still work in future versions, better set variables to null to help it."
  • Jon Skeet
    Jon Skeet over 15 years
    To say more about my last comment - this would require the C# team to break section 7.3.2 of the spec, for no good reason. Yes, using Flags is a good idea. No, it's really, really not necessary.
  • Scott Dorman
    Scott Dorman over 15 years
    Hmmm...looks like my last comment didn't make it...Jon is correct, using Flags doesn't cause the compiler to autogenerate proper power of 2 values, even in VB. (I just checked this.)
  • Jon Skeet
    Jon Skeet over 15 years
    James - you're right, sorry, I forgot that side of things. Good catch.
  • Scott Dorman
    Scott Dorman over 15 years
    @Jon Skeet, my comment about no guarantees about behavior was actually arguing for using the Flags attribute. Just because you can use an enum as if it were a flags enum without the attribute doesn't mean you should. There is no guarantee that those scenarios will always work in the future.
  • Scott Dorman
    Scott Dorman over 15 years
    By using the Flags attribute you explicitly tell the compiler and the runtime that you expect a certain behavior from the enum.
  • Jon Skeet
    Jon Skeet over 15 years
    Scott: Yes, I know you were arguing for it. And I agree it's a good idea. I just don't believe you really need it at all, and the "they might change the spec" argument is flawed, as you can apply it to anything else. They might redefine addition and subtraction - how do you guard against that?
  • Jon Skeet
    Jon Skeet over 15 years
    That's "don't really need it" for the bitwise side - for ToString and Parse it's necessary to get the right behaviour, and it's also good in terms of documenting expectations.
  • Jon Skeet
    Jon Skeet over 15 years
    Just looked through the spec, and I can't see any reference to the Flags attribute at all, so I don't believe it's affecting the compiler - just the framework.
  • Scott Dorman
    Scott Dorman over 15 years
    Looking at the code for Enum.Parse, it doesn't look like it takes the Flags attribute into account. If it gets a comma-separated list it will bitwise-or them together no matter what.
  • Scott Dorman
    Scott Dorman over 15 years
    Correct, right now it just affects the runtime. (Would be nice if the compiler validated the enum values, however.) In that sense, you are also correct in that you "don't really need it", but it does explicitly tell anyone who's looking how the enum should behave. There is no reason not to use it.
  • csakii
    csakii over 15 years
    [Flags] does seem like the way to go here.
  • Scott Dorman
    Scott Dorman over 15 years
    It was added to the runtime for a reason, specifically to indicate that an enumeration can be treated as a bit field. There are certain things you can't guard against being redefined (as in your examples) but there are things you can do to mitigate the results.
  • Jon Skeet
    Jon Skeet over 15 years
    Yes, it was added for a reason - but are you really going to suggest there's more than a million to one chance that the C# team will break the spec (which defines the behaviour very precisely) for no reason, and break potentially many apps?
  • Jon Skeet
    Jon Skeet over 15 years
    And yes, I agree there's no reason not to use it. I was only arguing against the "it might break in the future" logic :) Thanks for the catch about Parse, btw. Fixed my expanded explanation.
  • Jon Skeet
    Jon Skeet over 15 years
    Well, it's definitely a good thing to do - but it's entirely orthogonal to the actual question. It will neither help nor hinder the bitwise logic.
  • Scott Dorman
    Scott Dorman over 15 years
    This is exactly what flags enums (bit fields) are designed to do. From the MSDN page on the FlagsAttribute: Bit fields are generally used for lists of elements that might occur in combination...Therefore, bit fields are designed to be combined with a bitwise OR operation to generate unnamed values.
  • Scott Dorman
    Scott Dorman over 15 years
    No, I'm not suggesting that at all (although it happened moving from 1.1 -> 2.0). Anyway, the more likely scenario is that the compiler gains some knowledge about them and is able to do more compile time checks.
  • Mark Brackett
    Mark Brackett over 15 years
    Wow - I was completely wrong. It seems that Flags does not autogenerate the proper values for you in either VB or C# - nor will it complain if you then bitwise them. I'm not sure why I thought that was the case. Excuse me while I scurry off to look for any enum bugs over the last 5 years!
  • Nathan Koop
    Nathan Koop over 15 years
    You were correct about "Sunny" I needed to change it. Thanks for all the help everyone.
  • LawrenceF
    LawrenceF almost 7 years
    Aww, I always miss that step!