Refactor enum->class


Every so often, I see a C# user say they’d like to add a method to an enum.  Maybe it’s [Flags] and they want to verify that the combination of flags is legal according to their business rules.  Or maybe they’re in the process of moving to something more OO, involving inheritance instead of constants.


 


Anyway, I’ve been trying to think about how to do it.  I don’t know yet if there’s anything really compelling here, I’m still exploring.


 


The idea is a simple code transformation that gets you out of an enum and into a class, as simply as possible. 


 


In my experiments, I’m starting with this blob of code.  The challenge is to refactor it so that ‘E’ is not an enum, while maintaining basically the same semantics as when you started.


 



      enum E


      {


            a, b, c


      }


      class Program


      {


            void F(E e)


            {


                  E e2 = E.a;


 


                  switch (e)


                  {


                        case E.a:


                              break;


                        case E.b:


                              break;


                        case E.c:


                              break;


                  }


 


                  if (e == E.a)


                  {


                        return;


                  }


            }


      }


 


BTW, TheoY and I chatted about this today while I made tea, which is part of inspiration behind writing this entry.  


 


Edit: I want you to post your ideas about how you might refactor this.  Duh me for not saying so earlier!

Comments (6)

  1. Johny says:

    I stumbled onto this blog by accident, and found this post. This looked like fun; I hope I’m not stepping on any toes. Anyway, here’s my bid: common refactoring. The class/struct definitions are wordy but clear. Also, the importance of void Program::F( E *e ) is immediately undercut. As for semantics, the crucial one is that the behaviour-indicating type can be assigned a value and passed into methods. This is C++, though, and there may be simpler ways to do it in C# (and C++, for that matter). This can be refactored further, but it obscures the transformation.

    <pre>

    #include &lt;iostream&gt;

    using namespace std;

    struct E

    {

    virtual void foo() = 0;

    virtual void bar() {}

    };

    struct Ea

    : public E

    {

    virtual void foo() { cout << "a" << endl; }

    virtual void bar() { cout << "A" << endl; }

    };

    struct Eb

    : public E

    {

    virtual void foo() { cout << "b" << endl; }

    };

    struct Ec

    : public E

    {

    virtual void foo() { cout << "c" << endl; }

    };

    class Program

    {

    public:

    static void F( E *e )

    {

    Ea e2;

    e->foo();

    return e->bar();

    }

    };

    int main( int argc, char *argv[] )

    {

    Ea a;

    Program::F( &a );

    return 0;

    }

    </pre>

    I hope this comment is intelligible.

  2. Thomas Eyde says:

    public class C

    {

    public const int a = 0;

    public const int b = 1;

    public const int c = 2;

    private int id = -1;

    public static implicit operator int (C c)

    {

    return c.id;

    }

    public static implicit operator C (int i)

    {

    return new C(i);

    }

    public static bool operator == (C c, int i)

    {

    return (int)c == i;

    }

    public static bool operator != (C c, int i)

    {

    return (int)c != i;

    }

    private C(int id)

    {

    this.id = id;

    }

    }

  3. Thomas Eyde says:

    You did of course understand that the class C should be renamed to E.

    More details at http://home.online.no/~teyde/blog/Refactorenum-class.html

  4. jaybaz [MS] says:

    The answers you two provided match the two that we came up with. I wonder if there are any other interesting solutions out there?