Code Focused

Cool Case Clauses in Visual Basic and C#

Each language treats case clauses differently, but there's a way you can make them less boring in C#.

Case clauses in Visual Basic are cool. You can load them up with all kinds of expressions and method calls, allowing you to fine-tune your criteria:

Select Case patrons
  Case 1, 2
    ' ----- Small table: based on item list
  Case 3 To 6
    ' ----- Medium table: based on range
  Case ReturnSeven()
    ' ----- Large table: based on method call
  Case Is > 7
    ' ----- Extra large table: based on comparison
End Select

In C#, things aren't so cool. Each case clause is limited to a single constant value; no expressions, no methods, no comparison operators. If you want to process complex expressions like those in Visual Basic, you would normally use a series of if statements. You could retain the case clauses, but only by first processing the test value through some if-statement equivalent:

string testValue =
  (new int[] { 1, 2 }).Contains(patrons) ? "S" :
  patrons >= 3 & patrons <= 6            ? "M" :
  patrons == ReturnSeven()               ? "L" :
  patrons > 7                            ? "XL" :
  "";

switch (testValue)
{
  case "S":
    // ----- Small table code here.
    break;
  case "M":
    // ----- Medium table code here.
    break;
  case "L":
    // ----- Large table code here.
    break;
  case "XL":
    // ----- Extra large table code here.
    break;
}

But what if you wanted to be as cool as those Select Case users? Can you combine boring C# switch statements with conditional processing statements that are as feature-rich as those in Visual Basic? Perhaps by combining generics, lambda expressions and LINQ? Why, yes, yes you can. Let's generate the test variable for the switch statement by using LINQ to process a collection of conditional lambda expressions.

In this make-believe restaurant example, the goal has been to determine how large of a table to provide to a group of hungry patrons. To determine that, you need criteria functions that each accepts a customer count and indicates, true or false, whether the criteria for that table size is met. Let's create a delegate -- a method template -- that will help provide consistency in all criteria functions:

private delegate bool TableCountMatch(int eaters);

Next, you need a place to store all of these criteria functions, linking them to a target table size. You could build a custom generic class that tracks criteria functions and table sizes in tandem. But lucky for you, there's already a collection type that includes room for two values -- a dictionary:

Dictionary<TableCountMatch, string> testCases =
  new Dictionary<TableCountMatch, string>();

Dictionary collections require that the "key" part of the dictionary be unique. Because each criteria function will be its own unique analysis system, you'll use that for the unique key, storing the associated table size as the dictionary value:

testCases.Add((eaters) => (new int[] { 1, 2 }).Contains(eaters), "S");
testCases.Add((eaters) => eaters >= 3 & eaters <= 6,             "M");
testCases.Add((eaters) => eaters == ReturnSeven(),               "L");
testCases.Add((eaters) => eaters > 7,                            "XL");

Now you have a collection of criteria functions, stored as delegates, and matching table sizes that get spit out when a function returns true for a specific customer count. You just need the tool that does the spitting, and LINQ is good for that:

string testValue =
  (from scanTest in testCases
  where scanTest.Key(patrons)
  select scanTest.Value).First();

switch (testValue)
{
  case "S":
    // ----- And so on...

The query processes each entry in the dictionary, passing the patron count to the "key" function delegate, and returning the table size "value" when a function returns true. In case multiple criteria functions match the customer count, it's a good idea to wrap the query in an extension method that returns just the first match.

This code isn't perfect. Because the LINQ query checks all entries in the criteria dictionary, each entry will evaluate, even when an earlier criteria returns true. Allowing every criteria function to evaluate might have undesirable side effects. And yet, it's cool, and what's programming about if you can't enjoy a little cool from time to time?

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

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube