Click here to Skip to main content
11,925,292 members (60,701 online)
Click here to Skip to main content
Add your own
alternative version


14 bookmarked

Enum Builder: The Curious Case of the Inheritable Enum

, 1 Nov 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Proves that we can create a class that behaves like an inheritable enum
Father and son inherit from 'grandfather', so begin numbering after him.  Uncle and nephew form their own parallel branch, also numnbering after the grandfather.  Enum Builder can inherit and branch endlessly.


Programmers often express their frustration over the limitations of System.Enum. The worst of these is the failure of the Enum to expand and scale within the natural growth of a program's inheritance tree. Enum Builder eliminates this constraint.

This project also illustrates some powerful features of .NET:

  • It allows the end users to create their own 'enum' classes with a few lines of code, without even declaring a constructor, and yet they can enjoy powerful functionality. This is achieved through a strong interface and a robust abstract class.
  • It uses reflection and static fields to inject values into inheriting classes. This would seem intuitively impossible.
  • It behaves exactly like an Enum by implementing an indexer.
  • It implements the Singleton design pattern, though there are limitations due to the simple end class design.
  • It lazily auto-instantiates classes as needed to build proper enum-like values, so the end user never has to (and should not) ever call new.

Using the Code

  1. Copy Enum_Builder.dll from the folder Enum_Builder\bin\Release to your project folder.
  2. Add a reference to the builder. Use the Browse option and choose the Enum_Builder.dll you just copied.
  3. In any class where you wish to use an Enum Builder, import the namespace (the underscores indicate a namespace vs. a class):
  4. using Marcus_Technical_Services.Enum_Builder;
  5. Declare a class that inherits EnumBuilder. Decide on the access modifier; public is often wise if you intend to use these values elsewhere:
    public class EnumBuilder_Grandfather : EnumBuilder
  6. Add your 'enum' values as public static strings. Do not assign any values to these strings; Enum Builder will over-write them. In the example below, GRANDFATHER_HARRY will become 0, GRANDFATHER_BRUCE will get 1, etc.
  7. public class EnumBuilder_Grandfather : EnumBuilder
     public static string GRANDFATHER_HARRY;
     public static string GRANDFATHER_BRUCE;
  8. In this or any other class, you can inherit your own newly created Enum Builder by creating another two:
  9. public class EnumBuilder_Father : EnumBuilder_Grandfather
     public static string FATHER_BOB;
     public static string FATHER_CHARLES;
     public static string FATHER_WILLIAM;
     public static string FATHER_ALFRED;
    public class EnumBuilder_Son : EnumBuilder_Father
     public static string SON_JAMES;
     public static string SON_DYLAN;
     public static string SON_AVERY;
  10. At run-time, these values will begin after those of their base class, so FATHER_BOB will become 2, SON_JAMES will get 6, etc.
  11. Enum Builder also supports branching inheritance, so if you declare another set of classes that inherit from EnumBuilder_Grandfather, they will get the same numbering scheme as the father and son, since they are parallel:
  12. public abstract class EnumBuilder_Uncle : EnumBuilder_Grandfather
     public static string UNCLE_ABE;
     public static string UNCLE_JONAH;
     public static string UNCLE_TIM;
     public static string UNCLE_ALBERT;
    public abstract class EnumBuilder_Nephew : EnumBuilder_Uncle
     public static string NEPHEW_SIGGY;
     public static string NEPHEW_MAURY;
     public static string NEPHEW_ANTHONY;

    You can make any inheriting classes abstract. This is another potent feature of Enum Builder. The public static strings inside these last two classes will increment as usual, but they will not be public referenceable because they cannot be instantiated.

  13. Enum Builders can also be used in more exotic ways, such as with this internal example
  14. For testing, I inherited from nephew from the inheritance tree above. To challenge Enum Builder, I also nested this class inside of another class.

    public abstract class EnumBuilder_InternalUsage_Abs
     public abstract class EnumBuilder_Internal : EnumBuilder_Nephew
      public static string JULIE;
      public static string MARY;

    Next I inherited the abstract class above and inserted an inherited reference to the nested class above. The question is, will MARGARET increment beyond JULIE and MARY? (The answer is in the screen shot at the top of this article).

    public class EnumBuilder_InternalUsageTest : EnumBuilder_InternalUsage_Abs
     public class EnumBuilderInternal : EnumBuilder_InternalUsage_Abs.EnumBuilder_Internal
      public static string MARGARET;
      public static string PEGGY;
      public static string ALICE;
      public static string BEATRICE;
  15. Per the rules of transcendental meditation (sit perfectly still. If you must wiggle, wiggle): Do not muck with these classes. If you must disobey this precaution, do not add public static string for any other purpose except as stated here, as they will definitely get sucked and used as part of your 'enums'!
  16. There are a number of ways to use these newly created Enum Builders:
    • The Wrong Way

    • I include this because I know you are going to do it even if I ask you not to:

      EnumBuilder_Grandfather testGrandfather =
             new EnumBuilder_Grandfather();
      Console.WriteLine("Testing GRANDFATHER:");
      Console.WriteLine("       Trying Harry: ->" +
         testGrandfather[EnumBuilder_Grandfather.GRANDFATHER_HARRY] + "<-");
      Console.WriteLine("       Trying Bruce: ->" +
         testGrandfather[EnumBuilder_Grandfather.GRANDFATHER_BRUCE] + "<-");
      testGrandfather = null;

      Amazingly, this works, but it also creates a new instance of your Enum Builder repeatedly and without productive purpose. Note that you get the enum value by indexing ("[]") with the name of your custom class EnumBuilder_Grandfather and a dot, just as you would a 'normal' enum.

    • The Right Way -- Sort Of

    • You can create a local variable and assign it using the static instantiator from the abstract class EnumBuilder:

      EnumBuilder_Father testfather = EnumBuilder.Instance<EnumBuilder_Father>();
      Console.WriteLine("     Testing FATHER:");
      Console.WriteLine("         Trying Bob: ->" +
             testfather[EnumBuilder_Father.FATHER_BOB] + "<-");
      Console.WriteLine("     Trying Charles: ->" +
             testfather[EnumBuilder_Father.FATHER_CHARLES] + "<-");
      Console.WriteLine("     Trying William: ->" +
             testfather[EnumBuilder_Father.FATHER_WILLIAM] + "<-");
      Console.WriteLine("      Trying Alfred: ->" +
             testfather[EnumBuilder_Father.FATHER_ALFRED] + "<-");
      testfather = null;

      This is fine, but it does burn up a local variable that you must then dispose. Frankly, we don't need it.

    • The Real Right Way

    • Just refer to the static instantiator; it builds one instance of your custom Enum Builder class and thereafter reuses it (that's all in the internal plumbing):

      Console.WriteLine("        Testing SON:");
      Console.WriteLine("       Trying James: ->" +
         EnumBuilder.Instance<EnumBuilder_Son>()[EnumBuilder_Son.SON_JAMES] + "<-");
      Console.WriteLine("       Trying Dylan: ->" +
         EnumBuilder.Instance<EnumBuilder_Son>()[EnumBuilder_Son.SON_DYLAN] + "<-");
      Console.WriteLine("       Trying Avery: ->" +
         EnumBuilder.Instance<EnumBuilder_Son>()[EnumBuilder_Son.SON_AVERY] + "<-");

How It Works

Meaningful excerpts from the Enum Builder 'engine', the abstract class EnumBuilder:


To simplify our referencing of odd-looking types. Currently we cannot refer to an alias from another alias, hence some clunky moments below.

using TypeDictionary     = List<Type>;
using TypeDictionaries   = Dictionary<Type, List<Type>>;
using FieldDictionary    = List<string>;
using FieldDictionaries  = Dictionary<Type, List<string>>;
using Objects            = Dictionary<Type,Object>;
using EnumBuilder_IFaces = Dictionary<Type,EnumBuilder_IFace>;


Defines how to create a function pointer to an instance of EnumBuilder_IFace.

private delegate EnumBuilder_IFace       NewEnumBuilderDelegate(Type myType);

A variable pointing to the "new" instance creator, and is the actual delegate of the type above.

private static   NewEnumBuilderDelegate  returnNewEnumBuilderDelegate   = EnumBuilder.NewEnumBuilder;


For each inheriting type, store:

A list of the types starting above EnumBuilder and including itself.

private  static                      TypeDictionaries    typeStacks       = new TypeDictionaries();

A list of all fields in all relevant types, maintaining the exact order in which they were declared (bottom to top).

private  static                      FieldDictionaries   fieldStacks      = new FieldDictionaries();

A list of lockers to manage contention when we try to instantiate classes.

private  static  volatile  readonly  Objects             syncRootLocks    = new Objects();

A list of instances so we can (try to) enforce the Singleton pattern for each inheriting class.

private  static                      EnumBuilder_IFaces  singleInstances  = new EnumBuilder_IFaces();

Properties (Implemented from Enum_Builder_IFace)

The main indexer to get an enum-style integer value from a string. Enum Builder has already calculated the integer values of all public static strings entered by the programmer. This indexer searches the static Field_Stack to find out whether the string passed does indeed exist, and is so, returns the rote order (0 ->).

public int this[string enumName]
  if (NoFieldStackForThisType())

  if (String.IsNullOrEmpty(enumName))
   return IndexError(enumName, NULL_OR_EMPTY_WARNING);

  string upperEnumName = enumName.ToUpper();

  int foundIndex = Field_Stack(this.GetType()).IndexOf(upperEnumName);

  if (foundIndex >= 0)
   return foundIndex;
   return IndexError(enumName, DOES_NOT_EXIST_WARNING);

Emulates System.Enum GetNames: returns a list of the public static strings declared in this and all inherited classes.

 public FieldDictionary GetNames
   if (NoFieldStackForThisType())
    // Not exactly an error; we should not call Enum_Builder
    //    with a bunch of empty classes, theoretically...
    return null;

   return Field_Stack(this.GetType());

Methods (Static)

To enforce the Singleton design pattern, we expose a static method with constraints on who can call it. We only allow classes that implement our interface and that can be instantiated (they can call new).

public static T Instance<t>() where T : class, EnumBuilder_IFace, new()
 return (T)Instance_Internal(typeof(T), returnNewEnumBuilderDelegate);

A handy delegate to point to the method that will create an instance. We use the Activator, which is best for classes with no constructor parameters.

private static EnumBuilder_IFace NewEnumBuilder(Type myType)
 return (EnumBuilder_IFace)Activator.CreateInstance(myType);

Finds or creates a Singleton instance of any of our inherited classes.

 private static EnumBuilder_IFace Instance_Internal
		(Type myType, NewEnumBuilderDelegate myCreator)

  // If we have the instance, return it
  if (singleInstances.ContainsKey(myType))
   return singleInstances[myType];

  // ELSE create the instance

  // Find or lazily create a sync locker object
  if (!(syncRootLocks.ContainsKey(myType)))
   syncRootLocks.Add(myType, new Object());

  lock (syncRootLocks)
   // Redundant, but needed in highly contentious situations
   if (!(singleInstances.ContainsKey(myType)))
    singleInstances.Add(myType, myCreator(myType));

    // Success
    return singleInstances[myType];

  // The FAIL case
  return null;

The store-house and accessor for the 'enum' values that we equate with public static strings. Every inheriting type gets one of these (see the fieldStacks list as declared earlier). If Field_Stack is null, it will auto-instantiate itself for the current class, and once created, will not do so again.

 private static FieldDictionary Field_Stack(Type myType)
  // Build the type stack if it is null, then build the field stack.
  if (!(typeStacks.ContainsKey(myType)))
   // Create a list of integers for "myType".  It will never be used for another type.
   //	   We cannot do this inside "ReapTypeParents", as it is recursive.
   typeStacks.Add(myType, new TypeDictionary());

   // Create the list of types below us -- 
   //  The first parameter is what we are storing
   //  The second parameter is what we are targeting
   ReapTypeParents(myType, myType);

   // Create the list of public static string fields so we can index them

  // Returns the field stack for this type
  return fieldStacks[myType];

Gets the list of types below a given type, and insert them "backwards" so we can reap them and create a list of public static string fields in the proper order.

 private static void ReapTypeParents(Type myType, Type typeToReap)
  // If we hit the base abstract class "Enum_Builder", exit
  //    Should NEVER get to null before we hit the base abstract class
  if ((typeToReap == null) || (typeToReap == ENUM_BUILDER_ABSTRACT_TYPE))

  // Insert immediately in the first list position;
  //    on initial entry, this will be the original calling type
  //  on successive recursive passes,
  //  deeper classes will push their way up front at position 0.

  // NOTE that we can never add two of the same type,
  //  as we are traversing an inheritance tree from top to bottom.
  typeStacks[myType].Insert(0, typeToReap);

  // Call ourselves recursively; we have more types to seek
  ReapTypeParents(myType, typeToReap.BaseType);

Occurs once per type. Builds a stack of all declared public static strings, starting from the lowest inherited type up to the current type. The order of these strings is what allows Enum Builder to return that string's enum-equivalent integer value.

 private static void ReapTypeFields(Type myType)
  // Clear or (if needed) create the fields
  if (fieldStacks.ContainsKey(myType))
   fieldStacks.Add(myType, new FieldDictionary());

  // If the type stack is empty, leave
  if (!(typeStacks.ContainsKey(myType)))

  FieldInfo[] staticFields;
  string thisStaticFieldName;

  foreach (var thisType in typeStacks[myType])
   staticFields = thisType.GetFields(BindingFlags.Static | BindingFlags.Public);

   // Iterate through the found fields
   foreach (var thisStaticField in staticFields)
    // Get the instance name; convert to upper for safety
    thisStaticFieldName = thisStaticField.Name.ToUpper();

    // Confirm that this is a string
    if (thisStaticField.FieldType == STRING_FIELD_TYPE)
     // Push the name back to the field so it can be referred to 
     // programmatically (otherwise, this field value is null!)
     // We are not required to use the instance, 
     // since the fields being injected are static, so we pass NULL
     thisStaticField.SetValue(null, thisStaticFieldName);

     // If the static field name exists in the field stack, consider an error; 
     // this is fairly serious.
     // The programmer should not re-use public static string names 
     // in the inheritance tree.
     if (fieldStacks[myType].Contains(thisStaticFieldName))
	Enum Builder has found more than one copy of the public static string
                       field ->" + thisStaticFieldName + "<-.  
			Please change the field name or it will be IGNORED.");
      // Add the field; it is in the correct order already


The instance constructor has only one job: make sure that Field_Stack has been referred to so it can auto-instantiate itself. This is a classic corner case that I will discuss at the bottom of the article.

protected EnumBuilder()
 if (NoFieldStackForThisType()) {}

The static constructor sets the name of EnumBuilder's base abstract class so we can determine when we hit it (it is the "bottom" of all reaps).

static EnumBuilder()

Design limitations

Enum Builder numbers from 0 onward. I could add the functionality to start at any number, but this might prove more distracting than valuable. With so much inheritance going on, I am not sure it will end up being as clear as one might think. Virtually all uses of System.Enum simply require an index of 0 onward.

Challenges, Issues and Other Interesting Deviations

I thought creating Enum Builder would be easy: I'll just declare a static interface, set up static inheritance, inject static field values into static storage, and return the results. There would be no need for class instantiation, since it is not apparently necessary in the requirements.

Au contraire! I threw away a dozen versions of this project before arriving at the bizarre hybrid you see before you. In .NET, you cannot currently declare a static interface. OK, so I dropped that and began from an abstract class.

Arret! In .NET, you cannot declare a static indexer. This is true even if the class contains only static elements (sigh). I wasn't willing to ruin the programmer's experience over such a limitation.

I also didn't want to force the programmer to call new. Why use new if you just need to get an index of a static field? I almost got there, too, but for the dilemma of the order of operations in a .NET indexer. When you call:


Everything looks normal: Enum Builder receives the generic type myLocalEnumBuilder, reaps its inherited types and their fields, and stands ready with a 100% successful set-up for the request. Then you place a watch on the incoming indexer variable, myLocalEnumBuilder.SOME_ENUM. It is null. Merde!

It would seem that you cannot act soon enough to both build a set of static values and also make them available to a caller in time for them to pipe in as a method parameter. Or can you?

During much nervous futzing, and for no particular reason, I wandered down to the constructor and referred to the static Field_Stack (If != null, blah, blah). And wah-lah! My parameter had value. Here's why:

{curtain parts, revealing flustered, aging magician, out of breath}

  • We have to accept the fact that all end classes for using an Enum Builder will be non-abstract. We can use any number of abstract classes in between, just not at the end. No big deal.
  • We do need to stick with public static strings for the 'enums' because we need those to be processed and managed early, and only once per class.
  • Since all of the work gets done in the abstract EnumBuilder via static methods, those routines get called before any class ever gets instantiated.
  • The indexer needs an instance, but it can create its own, as it does in the sample call above. Hint: Look at the call to Activator.CreateInstance().
  • By the time any class creates its instance, it already has the values it needs to accurately pass a parameter. So the parameter is always valid using the current approach.


  • November 1st, 2009: Initial release, Version 1.0


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Software Developer (Senior) Marcus Technical Services, Inc.
United States United States
I have been designing and coding software applications for 25 years.

I am currently developing cutting-edge programs within the Dot Net platform and its contemporary foundations, WPF, WCF, WWF and Silverlight.

If you enjoy my articles, send some work my way:

You may also be interested in...

Comments and Discussions

RantIf it walks like a duck and talks like a duck it probably is a duck Pin
HightechRider12-Nov-09 7:18
memberHightechRider12-Nov-09 7:18 
GeneralRe: If it walks like a duck and talks like a duck it probably is a duck Pin
marcusts15-Nov-09 20:49
membermarcusts15-Nov-09 20:49 
GeneralThis really isn't a good replacement for enums Pin
GWBas1c4-Nov-09 20:24
memberGWBas1c4-Nov-09 20:24 
GeneralRe: This really isn't a good replacement for enums Pin
marcusts9-Nov-09 23:00
membermarcusts9-Nov-09 23:00 
GeneralRe: This really isn't a good replacement for enums Pin
GWBas1c10-Nov-09 14:52
memberGWBas1c10-Nov-09 14:52 
GeneralRe: This really isn't a good replacement for enums Pin
JalalAldeen8-Mar-10 10:57
memberJalalAldeen8-Mar-10 10:57 
GeneralMy vote of 2 Pin
GWBas1c4-Nov-09 20:12
memberGWBas1c4-Nov-09 20:12 
GeneralMy vote of 1 Pin
pdear2-Nov-09 16:16
memberpdear2-Nov-09 16:16 
GeneralRe: My vote of 1 Pin
marcusts2-Nov-09 19:18
membermarcusts2-Nov-09 19:18 
GeneralRe: My vote of 1 Pin
Member 39406323-Nov-09 6:30
memberMember 39406323-Nov-09 6:30 
GeneralMy vote of 2 Pin
CurtainDog2-Nov-09 15:01
memberCurtainDog2-Nov-09 15:01 
GeneralNo it doesn't Pin
PIEBALDconsult1-Nov-09 17:26
memberPIEBALDconsult1-Nov-09 17:26 
GeneralRe: No it doesn't Pin
marcusts2-Nov-09 19:20
membermarcusts2-Nov-09 19:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.151126.1 | Last Updated 1 Nov 2009
Article Copyright 2009 by marcusts
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid