Alignment with C# 8 Switch Expressions

Recently I’ve written two blog articles about the new C# 8 switch expression. I’ve changed my last blog article to a different indentation – so I’m wondering what version do you prefer?

Updated version with some more alignment options.

Choices

The first version of the switch expression is using the usual indentation as used with other C# code.

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
    (LightState.FlashingYellow, _) => (LightState.Red, currentLight),
    (LightState.Red, _) => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.Red) => (LightState.Green, currentLight),
    (LightState.Green, _) => (LightState.FlashingGreen, currentLight),
    (LightState.FlashingGreen, _) => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.GreenBlink) => (LightState.Red, currentLight),
    _ => (LightState.FlashingYellow, currentLight)
  };

In regard to the number of spaces, I’m using two spaces instead of four to reduce the line length. This makes code in books (and probably in blogs as well) easier to read.

In the second version, I’m using center-based alignment by moving the lambda operator of every single selection to the same position and align the left and right expressions accordingly.

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
                (LightState.FlashingYellow, _) => (LightState.Red, currentLight),
                           (LightState.Red, _) => (LightState.Yellow, currentLight),
           (LightState.Yellow, LightState.Red) => (LightState.Green, currentLight),
                         (LightState.Green, _) => (LightState.FlashingGreen, currentLight),
                 (LightState.FlashingGreen, _) => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.GreenBlink) => (LightState.Red, currentLight),
                                             _ => (LightState.FlashingYellow, currentLight)
  };

Another version that is also used in one sample in the Microsoft docs (most samples use the first version) is left alignment, but the lambda operators are aligned as well:

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
    (LightState.FlashingYellow, _)             => (LightState.Red, currentLight),
    (LightState.Red, _)                        => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.Red)        => (LightState.Green, currentLight),
    (LightState.Green, _)                      => (LightState.FlashingGreen, currentLight),
    (LightState.FlashingGreen, _)              => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.GreenBlink) => (LightState.Red, currentLight),
    _                                          => (LightState.FlashingYellow, currentLight)
  };

Moving the right side to the next line is another option mentioned in the comments. I’m adding this here. With longer lines, this option can be easier to read.

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
    (LightState.FlashingYellow, _)
      => (LightState.Red, currentLight),
    (LightState.Red, _)
      => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.Red)
      => (LightState.Green, currentLight),
    (LightState.Green, _)
      => (LightState.FlashingGreen, currentLight),
    (LightState.FlashingGreen, _)
      => (LightState.Yellow, currentLight),
    (LightState.Yellow, LightState.GreenBlink)
      => (LightState.Red, currentLight),
    _
      => (LightState.FlashingYellow, currentLight)
  };

With the using static directive, the enum type name can be removed accessing the enum members. This makes the lines shorter, and removes the need to split it to two lines. This is an extension to the idea of emmajoey’s comments:

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
    (FlashingYellow, _)  => (Red, currentLight),
    (Red, _)             => (Yellow, currentLight),
    (Yellow, Red)        => (Green, currentLight),
    (Green, _)           => (FlashingGreen, currentLight),
    (FlashingGreen, _)   => (Yellow, currentLight),
    (Yellow, GreenBlink) => (Red, currentLight),
    _                    => (FlashingYellow, currentLight)
  };

Martin Ullrich @dasmulli had the idea of spreadsheet alignment:

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
    (LightState.FlashingYellow, _)                     => (LightState.Red,            currentLight),
    (LightState.Red,            _)                     => (LightState.Yellow,         currentLight),
    (LightState.Yellow,         LightState.Red)        => (LightState.Green,          currentLight),
    (LightState.Green,          _)                     => (LightState.FlashingGreen,  currentLight),
    (LightState.FlashingGreen,  _)                     => (LightState.Yellow,         currentLight),
    (LightState.Yellow,         LightState.GreenBlink) => (LightState.Red,            currentLight),
    _                                                  => (LightState.FlashingYellow, currentLight)
  };

Using the using static directive, the line length could be reduced with spreadsheet alignment as well:

public (LightState Current, LightState Previous) GetNextLight(
  LightState currentLight, LightState previousLight)
  => (currentLight, previousLight) switch
  {
    (FlashingYellow, _)          => (Red,            currentLight),
    (Red,            _)          => (Yellow,         currentLight),
    (Yellow,         Red)        => (Green,          currentLight),
    (Green,          _)          => (FlashingGreen,  currentLight),
    (FlashingGreen,  _)          => (Yellow,         currentLight),
    (Yellow,         GreenBlink) => (Red,            currentLight),
    _                            => (FlashingYellow, currentLight)
  };

What do you think? Which alignment do you prefer for the switch expression?

Probably a Roslyn extension can be useful to automatically create a preferred indentation.

In the code snippet another position of a different Lambda operator is useful for discussion as well. Do you prefer the lambda operator to start the implementation of a method right at the end of the line of the method declaration, or starting it in the next line as I’ve used it here?

Enjoy learning and programming!

Christian

Links

Changing State with the Switch Expression (C# 8)

Moving from the switch statement to the switch expression (C# 8)

Image choice concepts ID 95308943 © Peshkova | Dreamstime

Advertisements

10 thoughts on “Alignment with C# 8 Switch Expressions

  1. I think the third option is easiest to read there, though you could also alias the ‘LightState’ enum to ‘ls’ or something to make it shorter and so make the whole thing a big less jagged.

    public (LightState Current, LightState Previous) GetNextLight(
    LightState currentLight, LightState previousLight)
    => (currentLight, previousLight) switch
    {
    (ls.FlashingYellow, _) => (ls.Red, currentLight),
    (ls.Red, _) => (ls.Yellow, currentLight),
    (ls.Yellow, ls.Red) => (ls.Green, currentLight),
    (ls.Green, _) => (ls.FlashingGreen, currentLight),
    (ls.FlashingGreen, _) => (ls.Yellow, currentLight),
    (ls.Yellow, ls.GreenBlink) => (ls.Red, currentLight),
    _ => (ls.FlashingYellow, currentLight)
    };

    Liked by 1 person

  2. First version is a difficult-to-read mess. Second version makes the righthand side easy to read, but makes the left side more difficult to read because it violates standard left-alignment. Third version is pretty nice, all around. It also reminds me of old C++ (and probably older) documentation styles. The grouped left alignment formatting per column of information keeps coming back around because it works.

    Liked by 1 person

  3. I choose option 4: None of the above.

    Line break before or after the arrow (choose to match code style in the rest of the project) and the lambda indented one level. Works even if the match or lamba becomes complex/long.

    Liked by 1 person

      1. It simply muddies the waters and makes a key facet of the language take more time to understand and maintain given that now we can all put even more personal flair on our syntax…. blech not only is it a waste of ms dev’s time but I’d say it’ hurts the language.

        Like

  4. To me it’s number 4 (or 1 if the line is short and simple). Not because they are the best ones to read, but because the other options look somehow unnatural for C# code. C# code usually flows from left to right where logically subordinated lines are indented once, but there are no further indentations within the line (or do I miss some cases?).

    Additionally, you can write 1 & 4 without the help of formatters. I like being independent of tools.

    And finally, 1 & 4 are closest to what F# match expressions look like. I think, when it comes to functional features, it’s a good idea to look at how it’s done in a functional language.
    https://fsharpforfunandprofit.com/posts/match-expression/#formatting-a-match-expression

    Thanks’ for the discussion! We really need to talk about things like that to find a common ground. Conventions make understanding code way easier.

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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