My understanding is that [Flag]
enums are typically used for things that can be combined, where the individual values aren't mutually exclusive.
For example:
[Flags]
public enum SomeAttributes
{
Foo = 1 << 0,
Bar = 1 << 1,
Baz = 1 << 2,
}
Where any SomeAttributes
value can be a combination of Foo
, Bar
and Baz
.
In a more complicated, real-life scenario, I use an enum to describe a DeclarationType
:
[Flags]
public enum DeclarationType
{
Project = 1 << 0,
Module = 1 << 1,
ProceduralModule = 1 << 2 | Module,
ClassModule = 1 << 3 | Module,
UserForm = 1 << 4 | ClassModule,
Document = 1 << 5 | ClassModule,
ModuleOption = 1 << 6,
Member = 1 << 7,
Procedure = 1 << 8 | Member,
Function = 1 << 9 | Member,
Property = 1 << 10 | Member,
PropertyGet = 1 << 11 | Property | Function,
PropertyLet = 1 << 12 | Property | Procedure,
PropertySet = 1 << 13 | Property | Procedure,
Parameter = 1 << 14,
Variable = 1 << 15,
Control = 1 << 16 | Variable,
Constant = 1 << 17,
Enumeration = 1 << 18,
EnumerationMember = 1 << 19,
Event = 1 << 20,
UserDefinedType = 1 << 21,
UserDefinedTypeMember = 1 << 22,
LibraryFunction = 1 << 23 | Function,
LibraryProcedure = 1 << 24 | Procedure,
LineLabel = 1 << 25,
UnresolvedMember = 1 << 26,
BracketedExpression = 1 << 27,
ComAlias = 1 << 28
}
Obviously a given Declaration
can't be both a Variable
and a LibraryProcedure
– the two individual values can't be combined.. and they're not.
While these flags are extremely useful (it's very easy to verify whether a given DeclarationType
is a Property
or a Module
), it feels "wrong" because the flags aren't really used for combining values, but rather for grouping them into "sub-types".
So I'm told that this is abusing enum flags – this answer essentially says that if I have a set of values applicable to apples and another set applicable to oranges, then I need a different enum type for apples and another one for oranges – the problem with that here is that I need all declarations to have a common interface, with DeclarationType
being exposed in the base Declaration
class: having a PropertyType
enum wouldn't be useful at all.
Is this a sloppy/surprising/abusive design? If so, then how is that problem typically solved?
Best Answer
This is definitely abusing enums and flags! It might work for you, but anybody else reading the code is going to be mightily confused.
If I understand correctly, you have a hierarchical classification of declarations. This is far to much information to encode in a single enum. But there is an obvious alternative: Use classes and inheritance! So
Member
inherits fromDeclarationType
,Property
inherits fromMember
and so on.Enums are appropriate in some particular circumstances: If a value is always one of a limited number of options, or if it is any combination of a limited number of options (flags). Any information which is more complex or structured than this should be represented using objects.
Edit: In your "real-life scenario" it seems there are multiple places where behavior is selected depending on the value of the enum. This is really an antipattern, since you are using
switch
+enum
as a "poor mans polymorphism". Just turn the enum value into distinct classes encapsulating the declaration-specific behavor, and your code will be much cleaner