|
|
|
It is sometimes useful to program your enums so that they can be several values at the same time. We call these multi-value enums "Flags". A Flavor enum would be a great place to permit multiple values, since food can be both Sweet and Sour. On the other hand, Month would be a stupid place to permit multiple values, because a month cannot be July and August at the same time. You can allow an enum to be several values at once by writing it as follows: [Flags()] enum TVStyle: int { None = 0, FlatScreen = 1, Monochrome = 2, HiDef = 4, AntiGlare = 8, Portable = 16, BuiltInDVD = 32, } Of immediate note is the weird "[Flags()]" thing that is now sitting just above the enum. This is simply the attribute that permits the enum to store multiple values. The other thing you'll notice is that we've assigned a very specific value each possible name in the enum. Remember that the underlying datatype of the enum is still int. If it's going to be possible to store any combination of values in our enum, then we need to make sure that every possible combination will result in a unique underlying value. If we increased each value by one, this wouldn't be the case. Storing the "2" and "4" values in the same enum would result in the same value as storing the "1" and "5" values in the same enum. How is C# supposed to tell the difference? It can't. In contrast, increasing by powers of two guarantees that there is only one combination of values that will yield any given number. Yes, you heard me, guarantees. If you don't believe me, just try getting the same number out of two different combinations! For those who are not familiar with binary math, you don't really need to understand the above paragraph to use flags. Just make sure that your enum values go up by powers of two, and you won't have to worry about it. However, it's probably in your best interests to learn how binary math works anyway, since it forms the foundation of modern computing. Besides, if your programmer colleagues find out that you don't understand binary math, they will laugh at you. You wouldn't want that, would you? Anyways, now that we've seen how to declare multiple value-capable enums, let's learn how to use them. First, we'll look at assigning values and testing for them: // Separate each value you are storing with a pipe symbol TVStyle style = TVStyle.Monochrome | TVStyle.Portable; // Test to see if style contains the HiDef value if( (style & TVStyle.HiDef) == TVStyle.HiDef ) Console.WriteLine("We're viewing hi-definition TV!"); else Console.WriteLine("Sorry, no hi-def for you."); // Add a style style |= TVStyle.BuiltInDVD; // Remove a style style ^= TVStyle.BuiltInDVD; Both of these operations use syntax that we haven't considered, before. Unlike || and &&, which are simple Boolean operators, | and & function at the binary level (yes, more binary math!) We use the | symbol to tack on an extra value to our enum variable, and & to test for a specific value. For our TVStyle variable, Monochrome contains a value of 2, and Portable contains a value of 16. When we use the | operator to "or" them together, the end result is an underlying integer value of 18. That just simple addition, and we can prove it by printing the variable's underlying value to the console: Console.WriteLine(((int)style).ToString()); As stated earlier, the fact that our possible values increase by powers of two guarantees that there's only one combination of values that will yield "18". In fact, we can assign an int value directly to an enum, and it will automatically figure out which enum values need to be stored to produce that number: style = (TVStyle)6; Console.WriteLine(style.ToString()); You will see that "style"'s values have changed to be Monochrome and HiDef, which "coincidentally" is the only combination of values that will yield 6, the value that we assigned to the variable. We can also parse out an enum from a string value: style = (TVStyle)Enum.Parse(typeof(TVStyle), "FlatScreen, HiDef, AntiGlare", true); When parsing from a string, we separate the values with ", ", instead of a |. That's most of the coding technique you need to know for using flagged enums. If you didn't understand all of the discussion above, you can still refer to the code sample below, which summarizes everything. Flagged enums aren't as important to understand as other C# features (I haven't used them nearly as much as their ordinary "one-value" cousins), but they're still pretty cool. using System; namespace C_EnumFlags { [Flags()] enum TVStyle:int { // Always go up by powers of 2, or some other // algorithmic increase that forbids the possibility // of duplicate values. Start at 0. // Because the first flag has a value of 0, it will // ALWAYS be true. Make it represent "None", so that // this doesn't matter. None = 0, FlatScreen = 1, Monochrome = 2, HiDef = 4, AntiGlare = 8, Portable = 16, BuiltInDVD = 32, } class Class1 { static readonly string ln = Environment.NewLine; [STAThread] static void Main(string[] args) { // Create a monochrome, portable TVStyle // Separate each extra enum value with a | ("pipe") symbol TVStyle style = TVStyle.Monochrome | TVStyle.Portable; Console.WriteLine(style.ToString()); // Parse a flatscreen, hi-def, anti-glare TVStyle // Assumes that each value is separated with a comma and space style = (TVStyle)Enum.Parse(typeof(TVStyle), "FlatScreen, HiDef, AntiGlare", true); Console.WriteLine(style.ToString()); // And here's the "under-the-hood" int value actually being stored in style Console.WriteLine(((int)style).ToString()); // We can even use an int to directly assign to style // 2 + 4 = 6, so assigning 6 will make style Monochrome and Hi-def // (A strange combination, to be sure, but a legitimate one!) style = (TVStyle)6; Console.WriteLine(style.ToString()); // Test a TVStyle for hi-def capability if( (style & TVStyle.HiDef) == TVStyle.HiDef ) Console.WriteLine("We're viewing hi-definition TV!"); else Console.WriteLine("Sorry, no hi-def for you."); PromptForExit(); } static void PromptForExit() { Console.Write(ln + "Program complete! Hit enter to exit..."); Console.ReadLine(); } } } |
|