DEV Community

riscie
riscie

Posted on

C# switch - Which variation do you prefer?

Let's say I have the following Enum:

public enum Cases
{
    One,
    Two,
    Three,
    Four,
    Five
}

And I would like to implement the following logic using switch:

One        => "one to three"
Two        => "one to three"
Three      => "one to three"
Four       => "four"
Five       => "five"
all others => throw an Exception

Before C# 8.0, I would have written my example using a classic switch statement:

Heads-up, if you don't know: [Theory] and [InlindeData] are Attributes used by xunit, a dotnet testing framework. You can ignore it, it's not important to the discussion I would like to initiate. (I had to make sure that my logic is correct, before we can discuss, right πŸ˜‰)

[Theory]
[InlineData(Cases.One, "one to three")]
[InlineData(Cases.Two, "one to three")]
[InlineData(Cases.Three, "one to three")]
[InlineData(Cases.Four, "four")]
[InlineData(Cases.Five, "five")]
public void SwitchStatement(Cases? input, string expected)
{
    string actual;
    switch (input)
    {
        case Cases.One:
        case Cases.Two:
        case Cases.Three:
            actual = "one to three";
            break;
        case Cases.Four:
            actual = "four";
            break;
        case Cases.Five:
            actual = "five";
            break;
        default:
            throw new InvalidEnumArgumentException();
    }

    Assert.Equal(expected, actual);
}

With C# 8.0 we did get switch expressions. Now we could write it like so:

[Theory]
[InlineData(Cases.One, "one to three")]
[InlineData(Cases.Two, "one to three")]
[InlineData(Cases.Three, "one to three")]
[InlineData(Cases.Four, "four")]
[InlineData(Cases.Five, "five")]
public void SwitchExpression(Cases? input, string expected)
{
    var actual = input switch
    {
        Cases.One => "one to three",
        Cases.Two => "one to three",
        Cases.Three => "one to three",
        Cases.Four => "four",
        Cases.Five => "five",
        _ => throw new InvalidEnumArgumentException()
    };

    Assert.Equal(expected, actual);
}

Or we can also combine cases One, Two and Three using when:

[Theory]
[InlineData(Cases.One, "one to three")]
[InlineData(Cases.Two, "one to three")]
[InlineData(Cases.Three, "one to three")]
[InlineData(Cases.Four, "four")]
[InlineData(Cases.Five, "five")]
public void SwitchExpressionWhen(Cases input, string expected)
{
    var actual = input switch
    {
        var x when x == Cases.One || x == Cases.Two || x == Cases.Three => "one to three",
        Cases.Four => "four",
        Cases.Five => "five",
        _ => throw new InvalidEnumArgumentException()
    };

    Assert.Equal(expected, actual);
}

We could also combine 1 to 3 using when in a diffrent variation using an Array and Contains():

[Theory]
[InlineData(Cases.One, "one to three")]
[InlineData(Cases.Two, "one to three")]
[InlineData(Cases.Three, "one to three")]
[InlineData(Cases.Four, "four")]
[InlineData(Cases.Five, "five")]
public void SwitchExpressionContains(Cases input, string expected)
{
    var actual = input switch
    {
        var x when new[] {Cases.One, Cases.Two, Cases.Three}.Contains(x) => "one to three",
        Cases.Four => "four",
        Cases.Five => "five",
        _ => throw new InvalidEnumArgumentException()
    };

    Assert.Equal(expected, actual);
}

❔ Which one of the examples do you prefer? Which one is the most readable to you? Or do you have an even nicer variation to share?

πŸ“Š I would like to gather the results in a poll. Do you mind giving your vote?

Top comments (6)

Collapse
 
peledzohar profile image
Zohar Peled • Edited

Michael B mentioned a dictionary option (though I don't know what it has to do with the strategy pattern) - and I second that - it's quite easy to do it like this:
(names are shortened for brevity - Never do that on actual code!)

private Dictionary<Cases, string> _dic = new Dictionary<Cases, string>()
{
    {Cases.One, "one to three"},
    {Cases.Two, "one to three"},
    // and so on...
}

string GetCorrespondingString(Cases input)
{
    return _dic.TryGetValue(input, out var result) ? 
        result : 
        throw new InvalidEnumArgumentException();
}

Since c# 7.0, you can throw an exception directly from inside a ternary condition because c# 7.0 supports throw expression.

However, the switch expression is a very powerful tool and has a potential to shorten and beautify what was once a big long mess of case: /* code */ break;

Collapse
 
riscie profile image
riscie

Thank you! I like the approach of using a dictionary. However this way you can not combine cases One to Three, right? In our example I would be fine with it, but If we would have many cases, I would kind of dislike it.

Collapse
 
peledzohar profile image
Zohar Peled

Yes, that will force you to write all the cases into the dictionary - and it is a shortcoming of this approach. There are advantages for both approaches, just choose the most appropriate one each time...

Collapse
 
sargalias profile image
Spyros Argalias

Very nice post.

My preference matches the exact order you wrote them. Last one is my favorite, first one is my least favorite, but I think they're all okay.

The later ones are more "functional", have less repetition, and look cleaner to me.

Collapse
 
katnel20 profile image
Katie Nelson

I feel the same.

Collapse
 
riscie profile image
riscie

Care to share a quick example?