Gotchas with switch expression

Introduction

I use JetBrains Rider for my development and usually refactor my code as per the tips from Rider (or ReSharper if you are using Visual Studio). One of the tips that Rider suggests is to replace the traditional switch-case statements with a relatively new C# feature, switch expression. Here is the screenshot of the suggestion:

Rider suggestion to convert switch-case to switch expression

If you are a Visual Studio user, then Roslyn analyzer gives the same refactoring suggestion.

Roslyn Analyzer screenshot for switch expression
Roslyn Analyzer refactoring suggestion

Most of the time, the refracting suggestion does not have any side-effect, and the code works as before.

Switch Expression refactoring may introduce a bug

Consider the below simple code:

Console.WriteLine("Is Null? " + (GetBoolean("Nah") == null));
enum Boolean
{
    Yes,
    No
}
static Boolean? GetBoolean(string boolString)
{
    switch (boolString)
    {
        case "Yes":
            return Boolean.Yes;
        case "No":
            return Boolean.No;
        default:
            return default;
    }
}

The method GetBoolean takes a string and returns a nullable enum. When the parameter boolString does not match any case, it returns default. That means when we pass parameter value as Nah the method returns null , and the output of the above code would be true

If we refactor the above code as per Rider or Visual Studio suggestion, the code would look as below:

static Boolean? GetBoolean(string boolString)
{
    return boolString switch
    {
        "Yes" => Boolean.Yes,
        "No" => Boolean.No,
         _ => default
     };
}

At first glance, the above code appears correct, and we would expect it to return null when we pass the parameter value as Nah as in the previous code snippet.

However, the method returns Yes instead. And the output of the code changes to false

Why does this happen?

I landed into this issue very recently, and luckily, a failed test helped me discover this bug. I raised this question on Twitter

The reason we see this behaviour is because, with switch expression, the first “case” does not return a Boolean? but Boolean instead. As a result, it infers the default as a default Boolean which is Yes leading to this bug.

@citizenmatt (Matt Ellis) sumps it up pretty nicely in his tweet

Matt Ellis (from JetBrains) and David Kean (from Visual Studio) were kind enough to raise a bug to fix this in the future release.

fixing the switch expression

To fix this issue, we can typecast the first case of switch expression as Boolean?.

static Boolean? GetBoolean(string boolString)
{
    return boolString switch
    {
        "Yes" => (Boolean?)Boolean.Yes,
        "No" => Boolean.No,
         _ => default
     };
}

Alternatively, we can return the default as null instead.

static Boolean? GetBoolean(string boolString)
{
    return boolString switch
    {
        "Yes" => Boolean.Yes,
        "No" => Boolean.No,
         _ => null
     };
}

Personally, I went for the second approach as I find it cleaner.

Wrapping Up

This whole exercise was a great learning exercise for me. It shows that we need to be careful when dealing with default type in C# especially when using it with switch expression. Hope you also find this useful.

Posted In

,
4 5 votes
Article Rating

Leave a Reply

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

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Huibert-Jan
Huibert-Jan
2 months ago

Any thoughts on returning default(Boolean?) over returning null?