In C#, how do I remove switch expressions?



This content originally appeared on DEV Community and was authored by Rahul Kumar Jha

Introduction

A number of online discussions can be found regarding the drawbacks of switch expressions or the recommendation to avoid using them altogether.

Several of us have likely received feedback on our pull requests urging us to eliminate switch expressions.

Here are some interesting discussions and blogs to check out:

Switch statements are bad?
Eliminating switch statements
Code Smells, Switch Statement

The purpose of this article is not to emphasize the pros and cons of using switch expressions.

Instead, let’s explore how we can restructure our code to eliminate switch expressions if necessary.

Code Setup

We begin by creating a basic console application that associates habits with pets.

Let’s add a class named Habit:

public class Habit
{
    public bool PlayFool { get; set; } = true;
    public bool Shy { get; set; } = true;
    public bool LoveColdShowers { get; set; } = true;
    public bool Lazy { get; set; } = false;
    public bool Bite { get; set; } = true;
    public bool Valid { get; set; } = true;
}

It consists of simple boolean properties like Shy and Lazy.

The next step is to add the enum Pet:

public enum Pet
{
    None,
    Cat = 1,
    Dog = 2,
    Rabbit = 3
}

Moving forward, let’s introduce a class called PetsHabit which contains public methods for retrieving the pet’s habits:

public class PetsHabit
{
    public Habit MapHabitsUsingSwitch(Pet pet)
    {
        return pet switch
        {
            Pet.Dog => new Habit
            {
                LoveColdShowers = false,
                Shy = false
            },
            Pet.Cat => new Habit
            {
                LoveColdShowers = false,
                Lazy = true,
                Shy = false,
            },
            Pet.Rabbit => new Habit
            {
                PlayFool = false,
                Lazy = true,
            },
            _ => new Habit { Valid = false }
        };
    }
}

PetsHabit includes the method MapHabitsUsingSwitch which takes a single parameter of type Pet.

MapHabitsUsingSwitch returns the pet’s habit and relies on a switch expression to generate a new habit.

Next, let’s investigate alternative approaches to refactoring from switch expressions while maintaining the same logic.

Refactoring Switch Expression with If

Although we can refactor the switch expression into if statements, it’s not the best choice.

Nevertheless, for the sake of exploration, let’s consider how we can transition from switch expressions to if statements.

Now, let’s add another method called MapHabitsUsingIf in the PetsHabit:

public Habit MapHabitsUsingIf(Pet pet)
{
    if (pet is Pet.Dog)
    {
        return new Habit
        {
            LoveColdShowers = false,
            Shy = false
        };
    }

    if (pet is Pet.Cat)
    {
        return new Habit
        {
            LoveColdShowers = false,
            Lazy = true,
            Shy = false,
        };
    }

    if (pet is Pet.Rabbit)
    {
        return new Habit
        {
            PlayFool = false,
            Lazy = true,
        };
    }

    return new Habit { Valid = false };

}

Instead of utilizing a switch expression, MapHabitsUsingIf employs if statements to determine the pet’s habit.

Certainly, if we need to refactor from switch expressions, substituting them with if statements is not the optimal solution.

This approach may introduce similar or even more drawbacks.

We must find a better approach – let’s examine that in the following section.

Switch Expression Refactoring with Delegate

There are various ways to refactor switch expressions, but the most common suggestion we encounter online is to utilize a strategy pattern.

public class PetsHabit
{
    public Habit MapHabitsUsingDelegate(Pet pet)
    {
        var habitsToFunctionMapper = GetHabitFunction(pet);

        if ( habitsToFunctionMapper.TryGetValue (
             pet, 
             out var getHabitFunbction)
           )
        {
            return getHabitFunbction();
        }
        return new Habit { Valid = false };
    }

    private Dictionary<Pet, Func<Habit>> GetHabitFunction(Pet pet)
    {
        return new Dictionary<Pet, Func<Habit>>
        {
            { Pet.Cat, this.GetCatHabit },
            { Pet.Dog, this.GetDogHabit },
            { Pet.Rabbit, this.GetRabbitHabbit },
        };
    }

}
private Habit GetCatHabit()
{
    return new Habit
    {
        LoveColdShowers = false,
        Lazy = true,
        Shy = false,
    };
}
private Habit GetDogHabit()
{
    return new Habit
    {
        LoveColdShowers = false,
        Shy = false
    };
}
private Habit GetRabbitHabbit()
{
    return new Habit
    {
        PlayFool = false,
        Lazy = true,
    };
}


This content originally appeared on DEV Community and was authored by Rahul Kumar Jha