Have you been burned by versioning enums (repost)


I did decide to do one repost, just to be sure everyone got a chance to give feedback...


Kit George is working on a guideline around versioning wrt Enums and he needs your feedback.  


 


It’s a know issue that adding values to enums is bad (from a breaking change perspective), WHEN someone is exhaustively switching over that enum. For example:


I have an enum, with three elements in it, and I have some API, which I have written to return a Color:


public enum Color { Red , Green, Blue };


public class Service {


public static Color GetColor() {


      // returns a valid Color


}


}


 


Someone consumes this enum by writing this (broken) code:


            Color c = Service.GetColor();


      switch (c) {


            case Color.Green :


                Console.WriteLine(“taking some action based on Green”);


                break;


            case Color.Blue :


                Console.WriteLine(“taking some action based on Blue”);


                break;


            default : // Just assume Red is the only other value


                Console.WriteLine(“taking some action based on Red”);


                break;


      }


 


The issue is: what happens when I want to add more colors to my enum? For example, I want to change Color, so that now, it has this definition:


public enum Color { Red , Green, Blue, Yellow, Purple };


 


Questions



  • Have you, or your team ever hit this problem with a managed API being updated in this way (an element added to an enum), and affecting your code?
  • Same question, but for an unmanaged API?
  • If you weren’t aware of this issue previously, do you think it might affect your code in the future?
  • What if we simply allow this kind of change: do you think it’s that bad?


Comments (9)

  1. SBC says:

    My inkling would be to use a typed collection. Using unmanaged calls may be addressed by that too. I’d reserve using ‘enums’ for situation when no changes to the enum list are expected. IMHO.. still learning..:-)

  2. Duncan says:

    Explicitly code each colour you have and have an error (unexpected colour) raised in the default.

  3. Steve says:

    Hi,

    I do this a lot – as our application expands. The way I handle that is to use the enum facility to effectively give me a custom datatype.

    When I add a new ‘state’ I simply rename the enum to something else and use the compiler warnings (in the ToDo) list to locate code that needs updating. Once I’ve located all the locations where the enum is used I rename it back.

    I’m not sure if that answers your question directly – or even if that’s the right way to do it – but it might help?

    In other words – it should be allowed, changes happen – but a warning should be generated until the coder acknowledges in some way the the breaking change has been handled. A bit like your obsolete flag – but it’s not obsolete – just changed.

    Another way is to generate a compiler warning when a select does not have all of the possible states in it – I think I saw this in Pascal once.

  4. Sami Vaaraniemi says:

    In my opinion an enum should be immutable. I think the design is improper if you have the need to add new values to an enum often.

    If you foresee that your type will evolve in the future, make it into a full-fledged (abstract) class and not an enum. Instead of using switch/case to do things, use virtual functions. Instead of adding new values to an enum, add new subclasses and override the virtual functions.

    My two cents

  5. Steve says:

    Well enums can’t truly be immutable with current compilersIDEs – at what point in the coding cycle do you lock them down?

    Technically it’s the same problem with abstract classes, what happens if someone changes the members within it?

    Using abstract classes gives you one form of version control (if you are strict with your control over classes).

    You’re applying guidelines over and above what the language is offering you. I guess the problem that is being solved here is how can that experience be built in to the language & compiler.

    What is really needed is some form of sandboxingversioning for codeinterfaces, a better way of manageing release management for interface design.

    After all this problem is going to get a lot worse with systems like Longhorn and even Web Services.

  6. OmReader says:

    MBA introduces a thundering feature named "Extensible Enum". I guess it was built to solve these kinds of problems.

    But i’ve never used it personaly except in ppt slides (no problemo quick RAD session – if by RAD we understand Read and dither 🙂 so i’m still waiting for real report based on concrete knowledge …

    Personaly i stop using enum for this type of tasks and prefer consider a way made of:

    Class C {

    const int EnumValue1 = 1;



    const int EnumValueN = N;

    const int EnumValueMin = 1;

    const int EnumValueMax = N;

    }

    you have at your disposition

    * typeof(C) for type casting

    * C.EnumValueMin <= x && x <= C.EnumValueMax for range checking

    you loose

    * StringConversion but who cares?

    * Implicit validation you must care !

    Reading your post i’m asking myselft if a solution made of

    * Designer and/or

    * Generator and/or

    * Attribute

    can be found to catch all demands without charge…

    If anyone has an idea?

  7. Simon Cooke says:

    Hmmm… what might be a good solution would be to have a sealed and unsealed version of an enum; sealed enums would not be extensible, whereas unsealed enums would have all of the possible dangers.

    The problem is that completely removing the possibility of having extensible enums removes the chance to gracefully extend or degrade functionality.

    Look at Win32; if it wasn’t possible to add one’s own WM_USER messages, it would be difficult to extend said functionality. Or HTML; tags which aren’t understood should be ignored.

    The trick would be that for enums which are extensible, one would have to enforce the authoring of a path which would handle unknown enum values. Whether this would be a simple throw of an exception, or code which has to be explicitly written is another matter entirely.

  8. OmReader says:

    Good remarks Simon

    It remind me another part of a problem i have faced and forget to recap.

    i allow ala WM_USER to reserve a range of free id. it’s to say that you could handle IDs by range and each range has an offset starting id that could be eitheir MOVABLE or not (inspired by win32 res)

    so if you don’t want to corrupt previous ID assignment you insert length-controlled free space that you are able to fill later or not and so on in a queue-like command IDs groups.

    another capacity was to compact space reaarranging all MOVABLE range at the end of free space (topological concept: nested segment)

    Am i clear enough?

    I think (its’ quite old now) you can have sealed (same pseudo-queue type) and unsealed (range can be add at the end)

  9. Matt Phillips says:

    For more than 15 years I’ve used the following guideline:

    When writing a switch statement for an enum, every enumeration must have an explicit case.

    The default is reserved for raising an assertion failure

Skip to main content