Published on

Enhance Readabilty with Enumeration Classes

Authors
  • avatar
    Name
    Samdanae Imran
    Twitter

Enums are a nice way to represent constant values, but they often lead to if-else/switch statements throughout code.

Enumeration Classes let you pre-define all the related constant properties of each enum. This allows strong-typing and easy access.

The Problem with Enums

// An enum that represents all possible values of a coin
enum Coin
{
    TenCents,
    TwentyCents,
    FiftyCents,
    OneDollar,
    TwoDollar
}

Let's say we want to get the total monetary value of some coins. It would go something like this:

public void PrintTotalAmount(List<Coin> coins) {
    decimal total = 0.0M;

    foreach (var coin in coins)
    {
        switch (coin)
        {
            Coin.TenCents:
                total += 0.10M
            Coin.TwentyCents:
                total += 0.20M
            Coin.FiftyCents:
                total += 0.50M
            Coin.OneDollar:
                total += 1.00M
            Coin.TwoDollars:
                total += 2.00M
            default:
        }
    }

    return $"The total is ${total}".
}

The drawback with this approach is that now we have code that's not cohesive. A coin's value is defined in code that is far away from the Coin's definition.

The Coin and it's value are very closely related, so we want to keep them as close as possible, ideally just a dot (.) away.

Enumeration Classes

Essentially, we want something like this:

public void PrintTotalAmount(List<Coin> coins) {
    decimal total = 0.0M;

    foreach (var coin in coins)
    {
        total += coin.Value; // We want the coin's value to stay with the coin.
    }

    return $"The total is ${total}".
}

This can be achieved easily by changing our enum to an enumeration class:

public class Coin {
    public static Coin TenCent {get;} = new(0.10M, "10c");
    public static Coin TwentyCent {get;} = new(0.20M, "20c");
    public static Coin FiftyCent {get;} = new(0.50M, "50c");
    public static Coin OneDollar {get;} = new(1.00M, "$1");
    public static Coin TwoDollar {get;} = new(2.00M, "$2");

    public string Label { get; }
    public decimal Value { get; }

    private Coin(decimal val, string name)
    {
        Value = val;
        Label = name;
    }
}

Click here to get the complete code for this post.

This allows you to use Coin the way you would an enum, while also getting easy access to extended properties for each coin.

Small Improvements

Now that you have more control over the enum, you can add helper properties or methods that assist in dealing with the enum values.

Here are two potential improvements that we could make to the Coin class:

  1. Add Ability To Refer To All Constant Values
  2. Add Value Converters

Add Ability To Refer To All Constant Values

public class Coin {
    public List<Coin> AllCoins { get; } = new List<Coin>();
    // ...
    public static Coin FiftyCent {get;} = new(0.50M, "50c"); // Will add to the AllCoins list
    // ...

    private Coin(decimal val, string name)
    {
        Value = val;
        Name = name;
        AllCoins.Add(this);
    }
    // ...
}

Add Value Converters

public class Coin {
    /// ...
    public static Coin FromString(string coinString)
    {
        return List().Single(r => string.Equals(r.Name, coinString, StringComparison.OrdinalIgnoreCase));
    }

    public static Coin FromValue(decimal value)
    {
        return List().Single(r => r.Value == value);
    }
    // ...
}

Final Code

public class Coin {
    public List<Coin> AllCoins { get; } = new List<Coin>();

    public static Coin TenCent {get;} = new(0.10M, "10c");
    public static Coin TwentyCent {get;} = new(0.20M, "20c");
    public static Coin FiftyCent {get;} = new(0.50M, "50c");
    public static Coin OneDollar {get;} = new(1.00M, "$1");
    public static Coin TwoDollar {get;} = new(2.00M, "$2");

    public string Label { get; }
    public decimal Value { get; }

    private Coin(decimal val, string label)
    {
        Value = val;
        Label = label;
        AllCoins.Add(this);
    }

    public static Coin FromString(string coinString)
    {
        return AllCoins.Single(r => string.Equals(r.Label, coinString, StringComparison.OrdinalIgnoreCase));
    }

    public static Coin FromValue(decimal value)
    {
        return AllCoins.Single(r => r.Value == value);
    }
}

Further Reading