US Capitol

Goodbye, NullReferenceException: Separate State in Separate Objects

So far in this series about NullReferenceException, we have used nullable operators and C# 8.0 Nullable References to avoid null and learned about the Option type as an alternative to null. Let’s see how to design our classes to avoid null when representing optional values.

Instead of writing a large class with methods that expect some nullable properties to be not null at some point, we’re better off using separate classes to avoid dealing with null and getting NullReferenceException.

Multiple state in the same object

Often we keep all possible combinations of properties of an object in a single class.

For example, on an e-commerce site, we create a User class with a name, password, and credit card. But since we don’t need the credit card details to create new users, we declare the CreditCard property as nullable.

Let’s write a class to represent either regular or premium users. We should only store credit card details for premium users to charge a monthly subscription.

public record User(Email email, SaltedPassword password)
{
    public CreditCard? CreditCard { get; internal set; }
    //              ^^^
    // Only for Premium users. We declare it nullable

    public void BecomePremium(CreditCard creditCard)
    {
        // Imagine we sent an email and validate credit card
        // details, etc
        //
        // Beep, beep, boop
    }

    public void ChargeMonthlySubscription()
    {
        // CreditCard might be null here.
        //
        // Nothing is preventing us from calling it
        // for regular users
        CreditCard.Pay();
        // ^^^^^
        // Boooom, NullReferenceException
    }
}

Notice that the CreditCard property only has value for premium users. We expect it to be null for regular users. And nothing is preventing us from calling ChargeMonthlySubscription() with regular users (when CreditCard is null). We have a potential source of NullReferenceException.

We ended up with a class with nullable properties and methods that only should be called when some of those properties aren’t null.

Inside ChargeMonthlySubscription(), we could add some null checks before using the CreditCard property. But, if we have other methods that need other properties not to be null, our code will get bloated with null checks all over the place.

Green grass near the gray road
Let's separate our state...Photo by Will Francis on Unsplash

Separate State in Separate Objects

Instead of checking for null inside ChargeMonthlySubscription(), let’s create two separate classes to represent regular and premiums users.

public record RegularUser(Email Email, SaltedPassword Password)
{
    // No nullable CreditCard anymore

    public PremiumUser BecomePremium(CreditCard creditCard)
    {
        // Imagine we sent an email and validate credit card
        // details, etc
        return new PremiumUser(Email, Password, CreditCard);
    }
}

public record PremiumUser(Email Email, SaltedPassword Password, CreditCard CreditCard)
{
    // Do stuff of Premium Users...

    public void ChargeMonthlySubscription()
    {
        // CreditCard is not null here.
        CreditCard.Pay();
    }
}

Notice we wrote two separate classes: RegularUser and PremiumUser. We don’t have methods that should be called only when some optional properties have value. And we don’t need to check for null anymore. For premium users, we’re sure we have their credit card details. We eliminated a possible source of NullReferenceException.

We’re better off writing separate classes than writing a single large class with nullable properties that only have values at some point.

I learned about this technique after reading Domain Model Made Functional. The book uses the mantra: “Make illegal state unrepresentable.” In our example, the illegal state is the CreditCard being null for regular users. We made it unrepresentable by writing two classes.

Voilà! This is another technique to prevent null and NullReferenceException by avoiding classes that only use some optional state at some point of the object lifecycle. We should split all possible combinations of the optional state into separate classes. Put separate state in separate objects.

Don’t miss the other posts in this series, what the NullReferenceException is and how to prevent it, Nullable Operators and References, and the Option type and LINQ XOrDefault methods.

If you want to practice the techniques and principles from this series with interactive coding listings and exercises, check my course Mastering NullReferenceException Prevention in C# on Educative. It covers all you need to know to prevent this exception in the first place.

Happy coding!