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?