- Published on
Enhance Readabilty with Enumeration Classes
- Authors
- Name
- Samdanae Imran
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:
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);
}
}