Code Focused

Pattern Matching in C# 7.0 Case Blocks

Welcome to the 21st century, C#, now that case blocks support a variety of pattern-matching formats.

Switch statements and the case blocks they contain have been a mainstay of C-language flow-control syntax since the initial incarnation of the language back in the 1970s. C# inherited the overall switch statement constructs from C, including its mundane constant-based selection process. Except for the requirement that a break statement appear at the end of each case block, little has changed in four decades. 

Visual Basic, by contrast, sports supercharged Case blocks that enable complex comparisons that are far more interesting than simple lists of constants:

Select Case age
  Case 50
    ageBlock = "the big five-oh"
  Case 80, 81, 82, 83, 84, 85, 86, 87, 88, 89
    ageBlock = "octogenarian"
  Case 90 To 99
    ageBlock = "nonagenarian"
  Case Is >= 100
    ageBlock = "centenarian"
  Case Else
    ageBlock = "just old"
End Select

C# 7.0, as part of Visual Studio 2017, brings switch statements into the 21st century, thanks to the new pattern-matching features added to case blocks. Three distinct matching formats are now possible. (Be aware that all samples listed in this article use the Visual Studio 2017 Release Candidate, and some elements might change before the final release.) The first format is the same tried-and-true constant syntax that has been around since the first C# release:

switch (age)
{
  case 50:
    ageBlock = "the big five-oh";
    break;
  case 80:
  case 81:
  case 82:
  // ... and so on ...
  case 89:
    ageBlock = "octogenarian";
    break;
  default:
    ageBlock = "just old";
    break;
}

The new type pattern enables matches based on a specific class or structure. These expressions include a type name followed by a new variable instance that can be accessed within the code of the matching case block. Null instances never match these expressions, and instead must be handled (if desired) using a null-constant matching block:

// ----- Assume that spaceItem is of type SpaceType,
//       and that Planet and Star derive from SpaceType.
switch (spaceItem)
{
  case Planet p:
    if (p.Type != PlanetType.GasGiant)
      LandSpacecraft(p);
    break;
  case Star s:
    AvoidHeatSource(s);
    break;
  case null:
    // ----- If spaceItem is null, processing falls here,
    //       even if it is a Planet or Star null instance.
    break;
  default:
    // ----- Anything else that is not Planet, Star, or null.
    break;
}

One implication of this syntax is that switch statements are no longer limited to core data types like integers and strings. You can include variables or expressions of any .NET type within both the switch statement and the case block expressions. Also, null is now a valid constant expression. It catches null reference-type objects, even if those same types are mentioned in a non-null fashion in other blocks.

The third format, known as var patterns, uses the var keyword, and simply copies the source test variable or expression into a new, named variable:

switch (testVariable)
{
  case var blockVariable:
    // ----- Use blockVariable here as needed.
    break;
}

This seems a bit pointless, until you add the when clause, also new to case blocks in C# 7.0. When attached to the end of a case block expression, the when keyword enables access to Visual Basic-style Case block comparisons:

switch (age)
{
  case 50:
    ageBlock = "the big five-oh";
    break;
  case var testAge when (new List<int>()
      { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 }).Contains(testAge):
    ageBlock = "octogenarian";
    break;
  case var testAge when ((testAge >= 90) & (testAge <= 99)):
    ageBlock = "nonagenarian";
    break;
  case var testAge when (testAge >= 100):
    ageBlock = "centenarian";
    break;
  default:
    ageBlock = "just old";
    break;
}

The when clause also works with type-style patterns:

switch (spaceItem)
{
  case Planet p when (p.Type != PlanetType.GasGiant):
    LandSpacecraft(p);
    break;
  case Planet p:
    // ----- Gas giants fall here.
    break;

Visual Studio 2015 and its associated C# 6.0 release included a similar when-clause pattern as part of the structured error-handling system:

// ----- Assumes a Boolean variable named hasOtherErrors
try
{
  // ----- Error-prone code here.
}
catch (Exception ex) when (hasOtherErrors == true)
{
  // ----- This isn't the first error.
}
catch (Exception ex)
{
  // ----- First-time error.
}

When logic branching requires anything more than a simple value-type comparison, C# developers have routinely reached for if-else statements to guide data processing. With the new case-block enhancements in C# 7.0 and Visual Studio 2017, switch statements have now become a reasonable option for expression-rich flow-control processing.

About the Author

Tim Patrick has spent more than thirty years as a software architect and developer. His two most recent books on .NET development -- Start-to-Finish Visual C# 2015, and Start-to-Finish Visual Basic 2015 -- are available from http://owanipress.com. He blogs regularly at http://wellreadman.com.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube