10

This strategy involves replacing the likes of this:

public class Politician
{
    public const int Infidelity = 0;
    public const int Embezzlement = 1;
    public const int FlipFlopping = 2;
    public const int Murder = 3;
    public const int BabyKissing = 4;

    public int MostNotableGrievance { get; set; }
}

With:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public class MostNotableGrievance
{
    public static readonly MostNotableGrievance Infidelity = new MostNotableGrievance(0);
    public static readonly MostNotableGrievance Embezzlement = new MostNotableGrievance(1);
    public static readonly MostNotableGrievance FlipFlopping = new MostNotableGrievance(2);
    public static readonly MostNotableGrievance Murder = new MostNotableGrievance(3);
    public static readonly MostNotableGrievance BabyKissing = new MostNotableGrievance(4);

    public int Code { get; private set; }

    private MostNotableGrievance(int code)
    {
        Code = code;
    }
}

Why exactly is this preferable to making the type an enumeration, like so:

public class Politician
{
    public MostNotableGrievance MostNotableGrievance { get; set; }
}

public enum MostNotableGrievance
{
    Infidelity = 0,
    Embezzlement = 1,
    FlipFlopping = 2,
    Murder = 3,
    BabyKissing = 4
}

There is no behavior associated with the type and if there was you would be using a different type of refactoring anyways, for example, 'Replace Type Code with Subclasses' + 'Replace Conditional with Polymorphism'.

However, the author does explain why he frowns on this method (in Java?):

Numeric type codes, or enumerations, are a common feature of C-based languages. With symbolic names they can be quite readable. The problem is that the symbolic name is only an alias; the compiler still sees the underlying number. The compiler type checks using the number 177 not the symbolic name. Any method that takes the type code as an argument expects a number, and there is nothing to force a symbolic name to be used. This can reduce readability and be a source of bugs.

But when trying to apply this statement to C#, this statement doesn't appear to be true: it won't accept a number because an enumeration is actually considered to be a class. So the following code:

public class Test
{
    public void Do()
    {
        var temp = new Politician { MostNotableGrievance = 1 };
    }
}

Will not compile. So can this refactoring be considered unecessary in newer high-level languages, like C#, or am I not considering something?

1
  • var temp = new Politician { MostNotableGrievance = MostNotableGrievance.Embezzlement }; Commented Apr 20, 2012 at 23:34

3 Answers 3

6

I think you've almost answered your own question there.

The second piece of code is preferred over the first because it offers type-safety. With the first piece, if you have a similar enumeration of Fish then you can say something like

MostNotableGrievance grievance = Fish.Haddock;

and the compiler won't care. If Fish.Haddock = 2 then the above will be exactly equivalent to

MostNotableGrievance grievance = MostNotableGrievance.FlipFlopping;

but obviously not instantly readable as such.

The reason enumerations are often no better is because the implicit conversion to and from int leave you with the exact same problem.

But, in C#, there is no such implicit conversion, so an enum is a better structure to use. You'll rarely see either of the first two approaches used.

5

If there's no behavior associated with the value and there's no extra information that you need to store alongside it and the language doesn't support implicit conversion to integers, I would use an enum; that's what they're there for.

1

Another example that might help you can be found in Effective Java (item 30, if you want to look it up). It is valid for C# as well.

Suppose you use int constants to model different animals, say snakes and dogs.

int SNAKE_PYTHON=0;
int SNAKE_RATTLE=1;
int SNAKE_COBRA=2;

int DOG_TERRIER=0;
int DOG_PITBULL=1;

Assume these are constants and maybe even defined in different classes. This may seem fine to you and it might actually work out. But consider this line of code:

snake.setType(DOG_TERRIER);

Clearly, this is an error. The compiler will not complain and the code will execute. But your snake won't turn into a dog. That is because compiler only sees:

snake.setType(0);

Your snake is actually a python (and not a terrier). This feature is called type safety and it is actually the reason why you're example code

var temp = new Politician { MostNotableGrievance = 1 };

won't compile. MostNotableGrievance is a MostNotableGrievance not an integer. With enums you can express this in the type system. And that is why Martin Fowler and I think that enumerations are great.

Not the answer you're looking for? Browse other questions tagged or ask your own question.