I have seen C code where people used enum heavily. But all it does is confuse others. In many places plain integers can do the same thing with less ambiguity. What are the common misuses of enum?
What are the common misuses of “enum” in C
cenum
Related Solutions
My understanding of the history of it is that it's based on two main points...
Firstly, the language authors preferred to make the syntax variable-centric rather than type-centric. That is, they wanted a programmer to look at the declaration and think "if I write the expression *func(arg)
, that'll result in an int
; if I write *arg[N]
I'll have a float" rather than "func
must be a pointer to a function taking this and returning that".
The C entry on Wikipedia claims that:
Ritchie's idea was to declare identifiers in contexts resembling their use: "declaration reflects use".
...citing p122 of K&R2 which, alas, I don't have to hand to find the extended quote for you.
Secondly it is actually really, really difficult to come up with a syntax for declaration that is consistent when you're dealing with arbitrary levels of indirection. Your example might work well for expressing the type you thought up off-the-bat there, but does it scale to a function taking a pointer to an array of those types, and returning some other hideous mess? (Maybe it does, but did you check? Can you prove it?).
Remember, part of C's success is due to the fact that compilers were written for many different platforms, and so it might have been better to ignore some degree of readability for the sake of making compilers easier to write.
Having said that, I'm not an expert in language grammar or compiler writing. But I know enough to know there's a lot to know ;)
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:
- Separate types for the hierarchy
Kingdom
,Phylum
, etc., which do not form inheritance hierarchy (otherwise,Phylum
could be assigned toKingdom
). Though they could inherit from a common base class. - Each expression like
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
. - Each member has to be a different type, so that
Animalia.Chordata
compiled, butPlantae.Chordata
didn't. - Each member needs to somehow know all its children, for the
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), where TChild
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-generic EnumSet
:
abstract class EnumSet
{}
abstract class EnumSet<TChild> : EnumSet where TChild : EnumSet
{
protected IEnumerable<TChild> Children { get; private set; }
public bool Contains(TChild child)
{
return Children.Contains(child);
}
}
Now we need to create a class for each level in the hierarchy:
abstract class Root : EnumSet<Kingdom>
{}
abstract class Kingdom : EnumSet<Phylum>
{}
abstract class Phylum : EnumSet
{}
And finally some concrete classes:
class Organisms : Root
{
public static readonly Organisms Instance = new Organisms();
private Organisms()
{}
public readonly Animalia Animalia = new Animalia();
public readonly Plantae Plantae = new Plantae();
}
class Plantae : Kingdom
{
public readonly Anthophyta Anthophyta = new Anthophyta();
}
class Anthophyta : Phylum
{}
class Animalia : Kingdom
{
public readonly Chordata Chordata = new Chordata();
}
class Chordata : Phylum
{}
Notice that children are always fields of the parent class. What this means is that to fill the Children
collection, we can use reflection:
public EnumSet()
{
Children = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public)
.Select(f => f.GetValue(this))
.Cast<TChild>()
.ToArray();
}
One problem with this approach is that Contains()
always works only one level down. So, you can do Organisms.Instance.Contains(animalia)
, but not .Contains(chordata)
. You can do that by adding overloads of Contains()
to the specific hierarchy classes, e.g.:
abstract class Root : EnumSet<Kingdom>
{
public bool Contains(Phylum phylum)
{
return Children.Any(c => c.Contains(phylum));
}
}
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.
Best Answer
One case I can think of, though haven't seen in the wild yet, is to abuse enums as a collection of disjointed integer constants:
The most pathological, innocent looking example I can imagine would be
because
one
==0,two
==1 etc.