I've come across a recurring issue in a few of my recent projects in which I find myself using enums to represent state, or type, or something else, and I need to check against a few conditions. Some of these conditions span multiple enums, so I end up with a load of logic in switch
and if
blocks, which I don't like. This is a particular problem when having to cast things to and from the enum, like when the enum is being used to check an int that you're getting from a web request or a calculation.
Is there something in C++ or C# that can be used as a nested enum? Something like this:
enum Animal
{
Mammal
{
Cat,
Dog,
Bear
},
Bird
{
Pigeon,
Hawk,
Ostrich
},
Fish
{
Goldfish,
Trout,
Shark
},
Platypus //srsly what is that thing
};
Obviously, it may or may not be declared like that, but you get the idea. The point is that in code you could use it like Animal thisAnimal = Animal.Mammal.Cat
and then later check if (thisAnimal.IsMember(Animal.Mammal))
or something like that.
I've seen Java's EnumSet, and found them pretty useful, but I don't think they're an exact match for the functionality I'm after. For this example, you'd have to declare an enum with all the animals at one level, and then add them all to the relevant sets. That would mean that when using the original enum, higher-level things like Mammal
or Invertebrate
would appear on the same "level" as something very specific like African Swallow
, which would imply that they were (to some degree) interchangeable, which isn't true. In theory, a nested structure as above might allow you to specify the level of "specificness" needed, so you could get this:
enum Animal::Kingdom.Order.Family.Genus.Species
{ /*stuff*/ }
Organism::Kingdom.Phylum.Class.Order.Family thisThing;
thisThing = Animalia.Cordata; //compiler error
thisThing = Animalia.Chordata.Aves.Passeri.Hirundinidae; //compiles OK
Does a structure like this exist anywhere? If not, how might I build one for C++ and/or C# and have it remain as generic and re-usable as possible?
Best Answer
I agree with others that this seems overengineered. Usually, you want either a simple enum or a complex hierarchy of classes, it's not a good idea to combine the two.
But if you really want to do this (in C#), I think it's useful to recap what exactly do you want:
Kingdom
,Phylum
, etc., which do not form inheritance hierarchy (otherwise,Phylum
could be assigned toKingdom
). Though they could inherit from a common base class.Animalia.Chordata.Aves
has to be assignable to a variable, which means we have to work with instances, not nested static types. This is especially problematic for the root type, because there are no global variables in C#. You could solve that by using a singleton. Also, I think there should be only one root, so the code above would become something likeOrganisms.Instance.Animalia.Chordata.Aves
.Animalia.Chordata
compiled, butPlantae.Chordata
didn't.IsMember()
method to work.The way I would implement these requirements is to start with a class like
EnumSet<TChild>
(though the name could be better), whereTChild
is the type of the children of this level in hierarchy. This class would also contain a collection of all its children (see later about filling it). We also need another type to represent leaf level of the hierarchy: non-genericEnumSet
:Now we need to create a class for each level in the hierarchy:
And finally some concrete classes:
Notice that children are always fields of the parent class. What this means is that to fill the
Children
collection, we can use reflection:One problem with this approach is that
Contains()
always works only one level down. So, you can doOrganisms.Instance.Contains(animalia)
, but not.Contains(chordata)
. You can do that by adding overloads ofContains()
to the specific hierarchy classes, e.g.:But this would be a lot of work for deep hierarchies.
After all of this, you end up with quite a lot of repetitive code. One way to fix that would be to have a text file that describes the hierarchy and use a T4 template to generate all the classes based on that.