8

I like switch expressions for mapping enums to values - while not the most scalable solution, it's fast and fairly clean if the enum represents something modal (and isn't huge).

One often source of bugs is adding members to an enum - which often leaves new cases unhandled.

But I think these bugs could be nearly wiped out if we could have compile errors for a non-exhaustive switch, making the omissions easily visible and fixable. (The default case has to be omitted though, otherwise it's all moot)

Is this possible? I'm thinking of something like this:

public string GetTargetValue()
{
    return target switch
   {
       Target.A => "foo",
       Target.B => "bar",
       // Compile error if someone added Target.C, otherwise works fine
       // no default case - it would defeat the point
   };
}

P.S: I work primarily in Unity, but to my understanding newer versions of Unity use the Roslyn compiler (I don't use burst) so I assume that doesn't matter.

Jonathan Levin
  • 594
  • 3
  • 13
  • 1
    Personally, I always add a ["default" part](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression) and then throw an exception. E.g. `_ => throw new ArgumentOutOfRangeException(nameof(direction), $"Unexpected target value '{target}'."),` – Uwe Keim Jul 02 '21 at 15:26
  • In addition the requirement to _not_ have a "default case" seems to me might be an [XY problem](https://xyproblem.info/). – Uwe Keim Jul 02 '21 at 15:29
  • An enum can be any value of the base type. It don't need to be a named value. Like `(Target)5` would work even is there no equivalent for 5 in the enum. So forcing all valid values in a switch seems extremely tedious. – Ralf Jul 02 '21 at 15:29
  • You can add CS8524 and CS8509 to the 'treat warnings as errors' section of your project – stuartd Jul 02 '21 at 15:30
  • Warning CS8524 was introduced specifically to capture this very pattern (CS8509 is for cases not involving enums). Make that an error and you're there. – Jeroen Mostert Jul 02 '21 at 15:30
  • Ralf - The example was meant to be some generic mapping, I didn't mean casting the enum to it's integer value, which would definitely be redundant. I've edited the question to show a slightly less trivial mapping. – Jonathan Levin Jul 02 '21 at 15:32
  • @JonathanLevin When the target variable has been set to `target = (Target)5;` before the shown method what should happen? – Ralf Jul 02 '21 at 15:34
  • @Ralf The compiler inserts its own default case, which throws. [See here](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA+ABATARgLABQGAzAATakDCpA3oaQ4wyaQJYB2ALqQOIycAVAIZQA5vwBqQgDYBXGAAphY/qU4jxnAJT0mjOgT1GMAdjUbVAZwDurTmAAWuo/ucu9yzQDoAgqQC8AHykOAA0bu6MnvxeAEIBwVjhhpF6APRpVBAAtgAOrNIwpDBQUNBsAGakljkwEOxFQgAmTTBNpNGcXpShpBCcDiW2lkXW0ADWlqQVHDARqRmk7BCkrRWyMtxgQiOkcGzcY7LS7WswQtwDRbkQHJzzegC+ANwPpI+EHwSELDDsstkOhZuDRSD5erFetRHkA===) – canton7 Jul 02 '21 at 15:36
  • @canton7 That's cool. – Ralf Jul 02 '21 at 15:41

1 Answers1

12

Yes you can.

This case raises the warning CS8509, as you can see here. To turn this into an error, add the following to your .editorconfig:

dotnet_diagnostic.CS8509.severity = error

You might also want to ignore CS8524, which happens if you don't have a default case, and CS8509 isn't being raised (so even if you're covering all possible values), see here. In this case, the compiler will insert a default case with throw new SwitchExpressionException(target):

dotnet_diagnostic.CS8524.severity = none

I suspect you probably need to be building with the .NET 5 SDK or higher for this warning to be generated.

canton7
  • 37,633
  • 3
  • 64
  • 77
  • Unfortunately it seems this warning is generated as long as the default case is missing, which would mean it would fail compilation no matter the enum values. – Jonathan Levin Jul 02 '21 at 15:36
  • 2
    @JonathanLevin No? If you include all cases, [you instead get CS8524](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA+ABATARgLABQGAzAATakDCpA3oaQ4wyaQJYB2ALqQOIycAVAIZQA5vwBqQgDYBXGAAphY/qU4jxnAJT0mjOgT1GMAdjUbVAZwDurTmAAWuo/ucu9yzQDoAgqQC8AHykOAA0bu6MnvxeAEIBwVjhhpEeFpxe1EGkxMmpegD0BVQQALYADqzSMKQwUFDQbABmpJZlMBDsNUIAJj0wPaTRGZShpBCcDnW2ljXW0ADWlqRNHDARqUWk7BCk/U2yMtxgQrOkcGzc87LSg/swQtyTNeUQHJwbegC+ANyfpF9CICCIQWDB2LJSkN0rRSD4xrExtQvkA===). You can choose to ignore that one, if you wish. – canton7 Jul 02 '21 at 15:37
  • 1
    Ah! I checked your link and saw similar warnings in each case, missed that they had different codes. Looks like this should work, thanks :) – Jonathan Levin Jul 02 '21 at 15:39