Multi-variable switch statement in C#

62,514

Solution 1

Yes. It's supported as of .NET 4.7 and C# 8. The syntax is nearly what you mentioned, but with some parenthesis (see tuple patterns).

switch ((intVal1, strVal2, boolVal3))
{
    case (1, "hello", false):
        break;
    case (2, "world", false):
        break;
    case (2, "hello", false):
        break;
}

If you want to switch and return a value there's a switch "expression syntax". Here is an example; note the use of _ for the default case:

string result = (intVal1, strVal2, boolVal3) switch
{
    (1, "hello", false) => "Combination1",
    (2, "world", false) => "Combination2",
    (2, "hello", false) => "Combination3",
    _ => "Default"
};

Here is a more illustrative example (a rock, paper, scissors game) from the MSDN article linked above:

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

Solution 2

You can do this in C# 7 and higher with the when keyword:

switch (intVal1)
{
    case 1 when strVal2 == "hello" && boolVal3 == false:
        break;
    case 2 when strVal2 == "world" && boolVal3 == false:
        break;
    case 2 when strVal2 == "hello" && boolVal3 == false:
        break;
}

Solution 3

There is (was) no built-in functionality to do this in C#, and I don't know of any library to do this.

Here is an alternative approach, using Tuple and extension methods:

using System;

static class CompareTuple {
    public static bool Compare<T1, T2, T3>(this Tuple<T1, T2, T3> value, T1 v1, T2 v2, T3 v3) {
        return value.Item1.Equals(v1) && value.Item2.Equals(v2) && value.Item3.Equals(v3); 
    }
}

class Program {
    static void Main(string[] args) {
        var t = new Tuple<int, int, bool>(1, 2, false);
        if (t.Compare(1, 1, false)) {
            // 1st case
        } else if (t.Compare(1, 2, false)) {
            // 2nd case
        } else { 
            // default
        }
    }
}

This is basically doing nothing more than providing a convenient syntax to check for multiple values - and using multiple ifs instead of a switch.

Solution 4

Let's look at this another way. If you have:

  • Very specific combinations you want to check for;
  • No comparisons to do;
  • A default handler for every non-matching case;
  • All primitive/value types (int, bool, string, etc.)

Then you can use a look-up table instead, which has a similar execution speed to the switch statement but not quite as efficient (since it needs to calculate hashes). Still, it's probably good enough. And it gives you the opportunity to name cases, to make this combinatorial explosion slightly less confusing and unmaintainable.

A code example:

private static readonly Tuple<int, int, bool> NameOfCase1 = 
    Tuple.Create(1, 1, false);
private static readonly Tuple<int, int, bool> NameOfCase2 =
    Tuple.Create(2, 1, false);
private static readonly Tuple<int, int, bool> NameOfCase3 =
    Tuple.Create(2, 2, false);

private static readonly Dictionary<Tuple<int, int, bool>, string> Results =
    new Dictionary<Tuple<int, int, bool>, string>
{
    { NameOfCase1, "Result 1" },
    { NameOfCase2, "Result 2" },
    { NameOfCase3, "Result 3" }
};

public string GetResultForValues(int x, int y, bool b)
{
    const string defaultResult = "Unknown";
    var lookupValue = Tuple.Create(x, y, b);
    string result;
    Results.TryGetValue(lookupValue, out result);
    return defaultResult;
}

If you need to actually execute a function or method for each case then you can use a result type (dictionary value) of Action<T> or Func<T> instead.

Note that I'm using Tuple<T1,T2,T3> here because it already has all of the hash code logic built in. The syntax is a little awkward in C# but if you want, you can implement your own lookup class and just override Equals and GetHashCode.

Solution 5

You could convert to a string:

switch (intVal1.ToString() + strVal2 + boolVal3.ToString())
{
   case "1helloFalse":
      break;
   case "2worldFalse":
      break;
   case "2helloFalse":

   etc ....
}

I think the question that comes to play, though is whether or not there's a better way of defining the logic. For instance, let's say you're trying to figure out who knows superman. We could do the check like this:

switch (first + last)
{
   case "ClarkKent":
   case "LoisLane":
      // YES
      break;
   default;
      // Sadly, no
      break;
}

But what happens when you get some other guy named Clark Kent? Really couldn't you have some other value that you determine this logic based on, ie bool KnowsSuperman?

The idea being, a switch statement is used to determine logic based off a single set of choices. If there are multiple values you're trying to switch off of, then the logic could get insanely difficult to maintain down the line.

Another example would be if you need to group people into several groups and perform some logic depending on the group they're in. You could code it up to say, if you're Bob, Jeff, Jim, or Sally, you're in group A, but what if you need to add someone else to group A? You'd have to change the code. Instead, you could create an extra property called Group, which could be an enum or string, which you could use to specify which group someone is in.

Share:
62,514

Related videos on Youtube

BanditoBunny
Author by

BanditoBunny

Developer

Updated on September 12, 2021

Comments

  • BanditoBunny
    BanditoBunny over 2 years

    I would like use a switch statement which takes several variables and looks like this:

    switch (intVal1, strVal2, boolVal3)
    {
       case 1, "hello", false:
          break;
       case 2, "world", false:
          break;
       case 2, "hello", false:
    
       etc ....
    }
    

    Is there any way to do something like this in C#? (I do not want to use nested switch statements for obvious reasons).

    The question was answered by .net dev team by implementing of exactly this fearture: Multi-variable switch statement in C#

  • BanditoBunny
    BanditoBunny over 12 years
    Hi, yes, I can, but this switch takes only one variable, I want to process three.
  • LoganS
    LoganS over 12 years
    @BanditoBunny - Then the answer is no you cannot do that.
  • John Saunders
    John Saunders over 12 years
    -1: correct answer, except for your switch. Remove it and I'll remove the downvote.
  • Raymond Chen
    Raymond Chen over 12 years
    I was looking at it more as a puzzle.
  • LoganS
    LoganS over 12 years
    Wrong != Possible - What you want isn't possible, so my solution isn't wrong - it just doesn't fit your combinatorial explosion :) (to put it nicely).
  • Sivvy
    Sivvy over 12 years
    If you create an enum for the values, this is VERY readable. Plus, as soon as I finished reading the question, the first thought in my head was using bitwise statements and flags.
  • BanditoBunny
    BanditoBunny over 12 years
    It's a possibility I also considered, however it is not nice :(
  • Security Hound
    Security Hound over 12 years
    @JonH - This does not even come close to doing what the author wants. Of course what he wants both cannot be done per the specification of the language nor should it. This does something entirely different, Raymond Chen's horrible looking code, came close to what the author wanted.
  • LoganS
    LoganS over 12 years
    @Ramhound- that's exactly my point...I know it doesn't do what the OP wants, because what the op wants is not possible.
  • Sivvy
    Sivvy over 12 years
    Especially when you change the question to now include a string as the second "parameter", when it was originally an int.
  • Aaronaught
    Aaronaught over 12 years
    Interesting quasi-functional version, although not type safe (and potentially not comparison safe). I might use IComparable or IEquatable instead of just object. A better version would use generics.
  • Jim Mischel
    Jim Mischel over 12 years
    +1. You went with Tuple and I went with the separate lookup class. I think I like your idea better.
  • Kjara
    Kjara over 6 years
    If it's all enums, you can write the case in a more readable way using + as it will still be evaluated at compile time. E.g. for enums Day and Month: Day d = Day.Wednesday; Month m = Month.February; switch ((int)d + 8*(int)m) { case (int)Day.Monday + 7*(int)Month.January: ... break; case (int)Day.Wednesday + 7*(int)Month.February: ... break; ...}
  • Pac0
    Pac0 almost 6 years
    that's a certainly less involved and more readable solution than most presented here, when you don't have too many cases, thanks to this new language feature.
  • Stephen Kennedy
    Stephen Kennedy over 5 years
    Indeed. Of course it could be made more concise by replacing boolVal3 == false with !boolVal3 (assuming it is a bool and not a nullable bool).
  • BanditoBunny
    BanditoBunny over 4 years
    That is more or less what i was looking for at that time, thanks for updating.
  • Vimes
    Vimes over 3 years
    For readers: it's now supported out of the box
  • Paolo Tedesco
    Paolo Tedesco over 3 years
    This answer is pretty old now :)
  • Vimes
    Vimes over 3 years
    I understand, not criticizing 🙂
  • Gavin Williams
    Gavin Williams about 3 years
    Maybe we should be able to flag these pages as out-of-date, still visible, but it needs to look really different to a normal question, like a big warning at the top or something. It's confusing to have all these old answers to old questions coming up, C# has changed somewhat in 10 years.
  • Vivek Gopalakrishnan
    Vivek Gopalakrishnan over 2 years
    Thanks for this. This is awesome.