Making enums smarter in C#

Vikas Sharma
Null Exception
Published in
4 min readAug 23, 2020

Creating enums to specify some type or category is very common in our day to day programming.

  • A user can be premium, standard, or normal.
  • Course material can be for beginners, intermediates, or experts.
  • Order can be pre-paid or cash-on-delivery.
  • Fixed deposits can be tax-saving, standard, or recurring.
  • A theme can be dark or light.

There are tons of examples and use-cases out there where we need to create enums while designing our system or models. Creating enums is very simple & fast and in no time you can express your business logic.

Well, using enums is so simple then why are we here? We are here to make enums smarter so as to make our code cleaner.

We often see code that usually tries to do something based on the enum passed to it.

public enum CustomerType
{
Regular = 1,
Gold = 2,
Premium = 3
}
public class Customer
{
public int Id { get; set; }
public CustomerType Type { get; set; }

public double CalculateDiscount(int amount)
{
switch (Type)
{
case CustomerType.Premium:
return amount * 0.30; // do some calculation
case CustomerType.Gold:
return amount * 0.20; // do some calculation
case CustomerType.Regular:
return amount * 0.10; // do some calculation
default:
return amount;
}
}

}

So, what’s wrong with this piece of code? Nothing! It is simple and does its job. Alright, if nothing is wrong in it then why we are talking about it? Because we can make this code more cleaner by adding a little bit of smartness to our enum. OK, but how to make an enum smart? ¯\_(ツ)_/¯ All an enum can do, represents some constant numeric values.

To make an enum smart, we need to add the capability to perform some actions to an enum.

Let’s see how to achieve that.

Get Smart

First of all, we gonna need an awesome NuGet package.

What’s this package do? It provides a base class from which we can inherit from, to make our enums. So the idea here is to convert the plain old simple enums to class and add some behavior to it. By converting enums to classes, we can move business conditions from the calling class to enum subclasses.

Get Set Go

Let’s create a class that will eventually hold our enum values, make sure to inherit it from SmartEnum class from SmartEnum NuGet package. SmartEnum class is a generic class where we need to specify the type of our class. Its constructor takes two parameters one is to name the enum value and the other is to hold the enum value.

Next, we need to add a method to enum class that will calculate the discount business logic but remembers that the discount calculation logic is going to be different for different customer types. So let’s add little strategy pattern to it.

The strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

Our base enum class will have an abstract method to calculate discount logic and different children classes will implement them as per their business rule.

If you see the class implementation closely, you will find that the base enum class (CustomerType) is abstract, because, we do not want any class to create an instance of it, rather use it as an enum, like we used to do with plain enums.

Also, we have made child classes private for the same reason stated above. The interesting thing about child classes is that they have their own enum value and behavior in the form of an overridden method.

Parent enum class will have child classes as its static readonly properties.

After that, our customer class will become very clean.

If you look closely, you will find that CalculateDiscount is now one-liner.

I know, I know, you must be thinking that all this trouble just to make our customer class cleaner but what about the enum classes, we had to write so many lines there? 🤷

Did we really need all that trouble?

The shorter answer is Yes.

Let’s understand why we needed all those changes when we could have achieved the same functionality by writing few lines of code.

It’s about maintainability, testability, and keeping our domain logic extensible. With this approach whenever a new customer type is introduced all we need to do, is to add a new class to hold the enum value and its business logic and that will have zero impact on our Customer domain model. By keeping SRP in our mind our enum class will do its job to calculate discount rather than Customer class has to worry about the business rule.

We added a little bit of object-oriented programming magic to make our enum smarter and our code cleaner.

Final Words

Only refactor when you feel there is complex decision making going on based on enum types. If your code is very simple then of course you don’t the overhead of adding an extra NuGet package. SmartEnum NuGet package can do a lot more than I’ve shown here, if you are planning to use it in your codebase do read its documentation.

Happy refactoring !! 😄

--

--