Multi-variable switch statement in C#
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 if
s 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.
Related videos on Youtube
Comments
-
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 over 12 yearsHi, yes, I can, but this switch takes only one variable, I want to process three.
-
LoganS over 12 years@BanditoBunny - Then the answer is no you cannot do that.
-
John Saunders over 12 years-1: correct answer, except for your switch. Remove it and I'll remove the downvote.
-
Raymond Chen over 12 yearsI was looking at it more as a puzzle.
-
LoganS over 12 yearsWrong != 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 over 12 yearsIf 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 over 12 yearsIt's a possibility I also considered, however it is not nice :(
-
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 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 over 12 yearsEspecially when you change the question to now include a string as the second "parameter", when it was originally an int.
-
Aaronaught over 12 yearsInteresting quasi-functional version, although not type safe (and potentially not comparison safe). I might use
IComparable
orIEquatable
instead of justobject
. A better version would use generics. -
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 over 6 yearsIf 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 enumsDay
andMonth
: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 almost 6 yearsthat'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 over 5 yearsIndeed. 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 over 4 yearsThat is more or less what i was looking for at that time, thanks for updating.
-
Vimes over 3 yearsFor readers: it's now supported out of the box
-
Paolo Tedesco over 3 yearsThis answer is pretty old now :)
-
Vimes over 3 yearsI understand, not criticizing 🙂
-
Gavin Williams about 3 yearsMaybe 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 over 2 yearsThanks for this. This is awesome.