Combinable enumerations in C# .NET
June 22, 2017 1 Comment
You’ve probably encountered cases with combined enum values using the pipe character, i.e. the “bitwise or” operator ‘|’:
Size.Large | Size.ExtraLarge
Let’s see an example of how to create such an enum.
The enumeration is decorated with the Flags attribute like in the following example:
[Flags] public enum SoftwarePositions { Architect = 0 , Developer = 1 , Programmer = 2 , FrontEnd = 4 , BackEnd = 8 , Database = 16 , DomainModeller = 32 }
Enums that are meant to be combined are usually pluralised, like here: SoftwarePositions instead of SoftwarePosition, although this is not a syntactical requirement. We provide explicit numeric values starting with 0 followed by a series of 2^n, i.e. 2^0 = 1, 2^1 = 2, 2^2 = 4 etc. This has got to do with how enumerations are stored in memory. E.g. architect is 0 so it is represented as all 0’s in 8 bits:
00000000
Programmer has the value 2 so in binary it will become
00000010
So if you are a database developer then you can use the following combination:
var databaseDeveloper = SoftwarePositions.Database | SoftwarePositions.Developer;
…and for a front-end programmer you can use the following:
var frontEndProgrammer = SoftwarePositions.Programmer | SoftwarePositions.FrontEnd;
Setting multiple flags will switch them on in the correct places in memory, e.g.:
01001100
You can check if somebody works with databases in the following ways:
bool worksWithDatabases = (databaseDeveloper & SoftwarePositions.Database) != 0; bool worksWithDatabasesAlt = databaseDeveloper.HasFlag(SoftwarePositions.Database);
…both will return true whereas the following will return false:
worksWithDatabases = frontEndProgrammer.HasFlag(SoftwarePositions.Database);
You can combine the combinations for do-it-all developers:
var doItAll = databaseDeveloper | frontEndProgrammer;
The Flags attribute will make sure that the following call results in a “nice” output:
Debug.WriteLine(doItAll);
…which prints
Developer, Programmer, FrontEnd, Database
Let’s say that the do-it-all developer stopped working with databases. We can remove that flag using the ^ operator:
var doSomeOfIt = doItAll ^ SoftwarePositions.Database; Debug.WriteLine(doSomeOfIt);
This will print
Developer, Programmer, FrontEnd
We can just as easily put it back if the person works with databases again:
doSomeOfIt ^= SoftwarePositions.Database; Debug.WriteLine(doSomeOfIt);
This will print
Developer, Programmer, FrontEnd, Database
You can remove a flag and not worry if the flag is present in the first place using a combination of the ~ and & operators:
var doItAllNoProgrammer = doItAll & (~SoftwarePositions.Programmer);
View all various C# language feature related posts here.
I think you have a problem here with the Architect value. Because it is 0 you can’t usefully combine it with the other values – there is no way to distinguish between
var devloper = SoftwarePositions.Developer;
and
var devloperAndArchitect = SoftwarePositions.Developer | SoftwarePositions.Architect;
Both variables will have the same value.
When using [Flags] all meaningful fields should be non-zero, and the value 0 should be used for some special field like “None”.
Otherwise a nice article.