Implementing inheritance with Enumeration class

Posted by

This is my fifth post in the Series: Enumeration classes – DDD and beyond. If you have jumped right in, I suggest going through my previous posts on the Enumeration class, especially the first post.

Inheritance is a critical concept of object-oriented programming. It helps us to reuse, extend, or change the behavior of a class. Unfortunately, the traditional Enums being a value type do not support inheritance. 

Enumeration class being a “class type” can help us get rid of this limitation. However, since the properties of the Enumeration class are static, the solution may not be pretty. In this post, I have tried to provide a couple of ways you can approach this problem.

NuGet and source code

The Enumeration class and other dependent classes are available as the NuGet packages. You can find the source code for the series at this GitHub link.

Problem Statement

Let us go back to our ever-green example of the PaymentType Enumeration class.

public abstract class PaymentType : Enumeration
{
public static readonly PaymentType DebitCard = new DebitCardType();
public static readonly PaymentType CreditCard = new CreditCardType();
public abstract string Code { get; }
private PaymentType(int value, string name = null) : base(value, name)
{
}
private class DebitCardType : PaymentType
{
public DebitCardType() : base(0, "DebitCard")
{
}
public override string Code => "DC";
}
private class CreditCardType : PaymentType
{
public CreditCardType() : base(1, "CreditCard")
{
}
public override string Code => "CC";
}
}
view raw PaymentType.cs hosted with ❤ by GitHub

Let us say that the organization goes global, and now the solution needs to work in many regions across the world. Specifically, the US region supports a new PaymentType BitCoin, while the rest of the world is still catching up. How do we introduce the BitCoin payment type for the US region alone?

Here are my couple of attempts to solve this problem.

Solution 1

In our first solution, we extend PaymentType by creating a derived class.

We adjust the PaymentType class by making the constructor protected.

protected PaymentType(int value, string name = null) : base(value, name)
{
}
view raw PaymentType.cs hosted with ❤ by GitHub

We then introduce StatesPaymentType for the US region derived from PaymentType.

public abstract class StatesPaymentType : PaymentType
{
public static readonly PaymentType Bitcoin = new BitCoinType();
private class BitCoinType : PaymentType
{
public BitCoinType() : base(3, "Bitcoin")
{
}
public override string Code => "BT";
}
protected StatesPaymentType(int value, string name = null) : base(value, name)
{
}
}
view raw StatesPaymentType.cs hosted with ❤ by GitHub

As you can see, this is a simple solution to extend the PaymentType with an additional option for the US.

Here are tests for our newly created StatesPaymentType Enumeration class.

public class InheritanceExample1Tests
{
[Fact]
public void CanReadAllPaymentTypesForUnitedStates()
{
Assert.Equal("CC", StatesPaymentType.CreditCard.Code);
Assert.Equal("DC", StatesPaymentType.DebitCard.Code);
Assert.Equal("BT", StatesPaymentType.Bitcoin.Code);
}
[Fact]
public void CommonPaymentTypesAreEqual()
{
Assert.Equal(PaymentType.CreditCard, StatesPaymentType.CreditCard);
Assert.Equal(PaymentType.DebitCard, StatesPaymentType.DebitCard);
}
[Fact]
public void CanReadAllPaymentTypesForRestOfTheWorld()
{
Assert.Equal("CC", PaymentType.CreditCard.Code);
Assert.Equal("DC", PaymentType.DebitCard.Code);
}
}

The solution is all good except, when you try to access the parent PaymentType values from StatesPaymentType, you will get a warning to use a base class qualifier.

Figure 1: Warning – Use base class qualifier

Solution 2

In our second attempt, we try to solve this problem by hiding parent property using a new modifier.

First, we update PaymentType further by making more members protected. 

public abstract class PaymentType : Enumeration
{
public static readonly PaymentType DebitCard = new DebitCardType();
public static readonly PaymentType CreditCard = new CreditCardType();
public abstract string Code { get; }
protected PaymentType(int value, string name = null) : base(value, name)
{
}
protected class DebitCardType : PaymentType
{
public DebitCardType() : base(0, "DebitCard")
{
}
public override string Code => "DC";
}
protected class CreditCardType : PaymentType
{
public CreditCardType() : base(1, "CreditCard")
{
}
public override string Code => "CC";
}
}
view raw PaymentType.cs hosted with ❤ by GitHub

Next, we update the StatesPaymentType Enumeration class is as below.

public abstract class StatesPaymentType : PaymentType
{
public new static readonly PaymentType DebitCard = new DebitCardType();
public new static readonly PaymentType CreditCard = new CreditCardType();
public static readonly PaymentType Bitcoin = new BitCoinType();
private class BitCoinType : PaymentType
{
public BitCoinType() : base(3, "Bitcoin")
{
}
public override string Code => "BT";
}
protected StatesPaymentType(int value, string name = null) : base(value, name)
{
}
}
view raw StatesPaymentType.cs hosted with ❤ by GitHub

Note that a new modifier is forbidden in general, as it can lead to some unexpected behavior and side-effects. However, in a scenario where we understand the risk, and there is no possible side effect, a new modifier can help solve some unique use-cases.

With this little tweak, we no longer get the warning as in the first solution. 

Conclusion

This post demonstrates how the Enumeration class can help you solve Enum type limitation with inheritance.

This post is also a wrap of my series on the Enumeration class. I hope you found this series useful. Please feel free to reach out to me on twitter or the comments section if you have any feedback. 🙂

Photo by Alberto Triano on Unsplash

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.