Flags are very similar to enums except that they are ordered like a bit field (i.e. increasing in powers of two starting at 1 by default). Flags can be defined with the flags keyword:
flags Pizza
{
Sauce,
Cheese,
Pepperoni,
Sausage,
Peppers,
Onion,
Mushroom,
Pineapple,
CanadianBacon,
Artichokes
}
The names flags enumeration line up with Integral Powers of two, the default starting value is 1
:
Console.WriteLine("Pizza.Sauce as Integer: `Pizza.Sauce as Integer`");
Console.WriteLine("Pizza.Cheese as Integer: `Pizza.Cheese as Integer`");
Console.WriteLine("Pizza.Pepperoni as Integer: `Pizza.Pepperoni as Integer`");
Console.WriteLine("Pizza.Sausage as Integer: `Pizza.Sausage as Integer`");
Console.WriteLine("Pizza.Peppers as Integer: `Pizza.Peppers as Integer`");
Console.WriteLine("Pizza.Onion as Integer: `Pizza.Onion as Integer`");
Console.WriteLine("Pizza.Mushroom as Integer: `Pizza.Mushroom as Integer`");
Console.WriteLine("Pizza.Pineapple as Integer: `Pizza.Pineapple as Integer`");
Console.WriteLine("Pizza.CanadianBacon as Integer: `Pizza.CanadianBacon as Integer`");
Console.WriteLine("Pizza.Artichokes as Integer: `Pizza.Artichokes as Integer`");
Pizza.Sauce as Integer: 1
Pizza.Cheese as Integer: 2
Pizza.Pepperoni as Integer: 4
Pizza.Sausage as Integer: 8
Pizza.Peppers as Integer: 16
Pizza.Onion as Integer: 32
Pizza.Mushroom as Integer: 64
Pizza.Pineapple as Integer: 128
Pizza.CanadianBacon as Integer: 256
Pizza.Artichokes as Integer: 512
Flags are meant to work together; consider the following:
var hawaiian : Pizza = Pizza.Sauce + Pizza.Cheese + Pizza.CanadianBacon + Pizza.Pineapple;
Console.WriteLine(hawaiian);
// Using bitwise and, '&', to determine if the hawaiian pizza has pepperoni on it
if ((hawaiian & Pizza.Pepperoni) != 0)
{
Console.WriteLine("Hawaiian pizza has `Pizza.Pepperoni` on it.");
}
else
{
Console.WriteLine("Hawaiian pizza does not have `Pizza.Pepperoni` on it.");
}
---------------- Begin Game ---------------
Sauce | Cheese | Pineapple | CanadianBacon
Hawaiian pizza does not have Pepperoni on it.
Notice how it prints all of the active flags
Basically, flags are enums that assign the next value by multiplying the previous value by two. For instance, given:
flags Cookie
{
ChocolateChips,
Raisins,
Oatmeal,
MacadamiaNuts = 3,
Peanuts,
Pecans,
Caramel,
Ginger,
Cinnamon,
Sugar = 3
}
The values are different:
Console.WriteLine("Cookie.ChocolateChips as Integer: `Cookie.ChocolateChips as Integer`");
Console.WriteLine("Cookie.Raisins as Integer: `Cookie.Raisins as Integer`");
Console.WriteLine("Cookie.Oatmeal as Integer: `Cookie.Oatmeal as Integer`");
Console.WriteLine("Cookie.MacadamiaNuts as Integer: `Cookie.MacadamiaNuts as Integer`");
Console.WriteLine("Cookie.Peanuts as Integer: `Cookie.Peanuts as Integer`");
Console.WriteLine("Cookie.Pecans as Integer: `Cookie.Pecans as Integer`");
Console.WriteLine("Cookie.Caramel as Integer: `Cookie.Caramel as Integer`");
Console.WriteLine("Cookie.Ginger as Integer: `Cookie.Ginger as Integer`");
Console.WriteLine("Cookie.Cinnamon as Integer: `Cookie.Cinnamon as Integer`");
Console.WriteLine("Cookie.Sugar as Integer: `Cookie.Sugar as Integer`");
Cookie.ChocolateChips as Integer: 1
Cookie.Raisins as Integer: 2
Cookie.Oatmeal as Integer: 4
Cookie.MacadamiaNuts as Integer: 3
Cookie.Peanuts as Integer: 6
Cookie.Pecans as Integer: 12
Cookie.Caramel as Integer: 24
Cookie.Ginger as Integer: 48
Cookie.Cinnamon as Integer: 96
Cookie.Sugar as Integer: 3
Notice how after MacadamiaNuts is set to 3, the increment is no longer on powers of 2
The underlying structure is integer; therefore, you could interpret Integers as a flags type:
var random: Pizza = 583 as Pizza;
Console.WriteLine(random);
Console.WriteLine(7 as Cookie);
Sauce | Cheese | Pepperoni | Mushroom | Artichokes
ChocolateChips | Raisins | Oatmeal | MacadamiaNuts | Peanuts | Sugar
Notice how flags print both enumerated values if the flags share the same underlying integer value.
Flags Rules
Identifier Capitalization
Identifiers for the flags type name and the enumerated values must be capitalized.
// This will not compile.
flags Directions {up, down, left, right}
Enum declaration 'Directions' does not have a closing '}'. We found 'LowerIdentifier' but we
expected to find '}'.
// Neither will this.
flags directions {Up, Down, Left, Right}
Enum declaration is missing a name. Upper-camel case names are required here (use 'Directions'
instead of 'directions').
Unique Identifiers
Identifiers for the enumerated values must be unique.
// This will not compile.
flags Directions {Up, Down, Up, Down}
A value of the same name 'Up' has already been declared in the enum 'Directions'. Names must
only be used once.
Definition Scope
Flags cannot be defined inside classes, structs, or other enums.
// Classes cannot contain flags.
class FlagsWrapper
{
// This won't compile.
flags Directions {Up, Down, Left, Right};
}
Class declaration 'FlagsWrapper' does not have a closing '}'. We found 'flags' but we expected to find '}'.
Representation
If there is no enumerated value the enum is represented as an Integer:
Console.WriteLine((64) as Cookie);
64
Flags and Integers
As in other languages, Integers make up the backbone of how flags work, and thus they work together in many situations.
Casting
Because of this, Nada will implicitly cast from a flag to an Integer when necessary:
class FlagsExample
{
[Static]
// This function accepts an flags type.
function AcceptsFlags(param: Pizza)
{
Console.WriteLine(" turned into `param`!");
}
[Static]
// This function accepts an integer type.
function AcceptsInt(param: Integer)
{
Console.WriteLine(" turned into `param`!");
}
}
We can test the calls to these functions to see the interplay of casting:
var paramInt = 128;
var paramFlags = Pizza.Pineapple;
// This works, as you'd expect
Console.Write(paramFlags);
FlagsExample.AcceptsFlags(paramFlags);
// But if you want to accept an Integer in an enum's place you MUST cast.
Console.Write(paramInt);
*FlagsExample.AcceptsFlags(paramInt); * Doesn't compile
FlagsExample.AcceptsFlags(paramInt as Pizza);
Pineapple turned into Pineapple!
128 turned into Pineapple!
// Attempting to pass an Integer without casting to an enum
// will cause a compiler error
Console.Write(paramInt);
FlagsExample.AcceptsFlags(paramInt);
The function 'AcceptsFlags' exists, but could not be resolved since the
types of the arguments used did not match. The arguments you gave were:
AcceptsFlags(Integer)
The possible choices were:
AcceptsFlags(param : Pizza)
// This also works as you'd expect.
Console.Write(paramInt);
FlagsExample.AcceptsInt(paramInt);
// The flags type parameter is implicitly casted to an Integer and is then passed to the function.
Console.Write(paramFlags);
FlagsExample.AcceptsInt(paramFlags);
128 turned into 128!
Pineapple turned into 128!
Flag-Integer Operations
Flags can perform all operations as if they were Integers. For instance, you can perform all of the normal math operations as implicit conversion will happen as necessary from the enum type to Integer:
// Here you can see use of the +, -, *, /, (), ^ and % Integer operators.
var pizza = (Pizza.Sauce + Pizza.Cheese) ^ Pizza.Sausage %
Pizza.Mushroom - Pizza.Artichokes * Pizza.Peppers;
// When the math evaluates to an unaliased number, the variable, although still of the enum type,
// prints out its integer value.
Console.WriteLine(pizza);
Console.WriteLine(typeid(pizza).Name);
Sauce | Onion (-8159)
Pizza
You can see the typeid is not an Integer but Pizza. An Integer value was printed because there were flags with no alias in that maps all aspects of -8159
. Because the type is still of the flags, you could pass it to our FlagsExample.AcceptsFlags
function without issue. When an operation includes both a flags and an Integer the result is always promoted to the flags type. If you want an Integer either
- Declare the variable you are storing the result in as an Integer:
var result : Integer
- Cast it:
result as Integer
Caveat
Although flags cast to Integers, they will not cast to Integers to perform cross-flags operations.
// This won't compile.
Console.WriteLine(Pizza.Pineapple + Cookie.Sugar);
The binary '+' operator 'Positive / Add' is not valid with 'Pizza' and 'Cookie'.