c#中的多变量开关语句

我想使用一个带有几个变量的switch语句,如下所示:

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

有没有办法在C#中做这样的事情? (出于显而易见的原因,我不想使用嵌套的switch语句)。

在C#中没有内置function来执行此操作,我不知道有任何库可以执行此操作。

这是一种替代方法,使用Tuple和扩展方法:

 using System; static class CompareTuple { public static bool Compare(this Tuple 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(1, 2, false); if (t.Compare(1, 1, false)) { // 1st case } else if (t.Compare(1, 2, false)) { // 2nd case } else { // default } } } 

这基本上只是提供一个方便的语法来检查多个值 – 并使用多个ifs而不是开关。

让我们看看另一种方式。 如果你有:

  • 您想要检查的非常具体的组合;
  • 没有比较;
  • 每个不匹配案例的默认处理程序;
  • 所有原始/值类型( intboolstring等)

然后,您可以使用查找表 ,它具有与switch语句类似的执行速度,但效率不高(因为它需要计算哈希值)。 不过,它可能还不错。 并且它为您提供了命名案例的机会,使这种组合爆炸稍微减少混乱和不可维护。

一个代码示例:

 private static readonly Tuple NameOfCase1 = Tuple.Create(1, 1, false); private static readonly Tuple NameOfCase2 = Tuple.Create(2, 1, false); private static readonly Tuple NameOfCase3 = Tuple.Create(2, 2, false); private static readonly Dictionary, string> Results = new Dictionary, 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; } 

如果您需要为每个案例实际执行一个函数或方法,那么您可以使用ActionFunc的结果类型(字典值)。

请注意,我在这里使用Tuple ,因为它已经内置了所有哈希代码逻辑。语法在C#中有点尴尬但是如果你愿意,你可以实现自己的查找类而且只是覆盖EqualsGetHashCode

我彻头彻尾的疯狂对此:

 class Program { static void Main(string[] args) { var i = 1; var j = 34; var k = true; Match(i, j, k). With(1, 2, false).Do(() => Console.WriteLine("1, 2, 3")). With(1, 34, false).Do(() => Console.WriteLine("1, 34, false")). With(x => i > 0, x => x < 100, x => x == true).Do(() => Console.WriteLine("1, 34, true")); } static Matcher Match(T1 t1, T2 t2, T3 t3) { return new Matcher(t1, t2, t3); } } public class Matcher { private readonly object[] values; public object[] Values { get { return values; } } public Matcher(T1 t1, T2 t2, T3 t3) { values = new object[] { t1, t2, t3 }; } public Match With(T1 t1, T2 t2, T3 t3) { return new Match(this, new object[] { t1, t2, t3 }); } public Match With(Func t1, Func t2, Func t3) { return new Match(this, t1, t2, t3); } } public class Match { private readonly Matcher matcher; private readonly object[] matchedValues; private readonly Func matcherF; public Match(Matcher matcher, object[] matchedValues) { this.matcher = matcher; this.matchedValues = matchedValues; } public Match(Matcher matcher, Func t1, Func t2, Func t3) { this.matcher = matcher; matcherF = objects => t1((T1)objects[0]) && t2((T2)objects[1]) && t3((T3)objects[2]); } public Matcher Do(Action a) { if(matcherF != null && matcherF(matcher.Values) || matcher.Values.SequenceEqual(matchedValues)) a(); return matcher; } } 

你可以转换为字符串:

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

我认为要发挥的问题是,是否有更好的方法来定义逻辑。 例如,假设您正在试图找出谁知道超人。 我们可以这样检查:

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

但是当你得到一个名叫克拉克肯特的其他人时会发生什么? 你真的不能拥有一些你确定这个逻辑的其他价值,即bool KnowsSuperman?

想法是,switch语句用于根据一组选择确定逻辑。 如果您尝试关闭多个值,则逻辑可能难以维持下线。

另一个例子是,如果你需要将人们分成几个小组并根据他们所在的小组执行一些逻辑。你可以编写代码说,如果你是Bob,Jeff,Jim或者Sally,你就是在A组中,但如果您需要将其他人添加到A组,该怎么办? 你必须改变代码。 相反,您可以创建一个名为Group的额外属性,该属性可以是枚举或字符串,您可以使用它来指定某人所在的组。

您可以使用when关键字在C#7及更高版本中执行此操作:

 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; } 

根据C#语言规范, switch语句表达式必须解析为sbyte,byte,sbyte,byte,short,ushort,int,uint,long,ulong,char,string或enum-type之一 。 这意味着您无法打开Tuple或其他更高阶类型。

假设有空间,您可以尝试将值组合在一起。 例如,假设每个整数保证在0..9范围内。

 switch (intVal1 * 100 + intVal2 * 10 + (boolVal3 ? 1 : 0)) { case 100: /* intVal1 = 1, intVal2 = 0, boolVal3 = false */ ... break; case 831: /* intVal1 = 8, intVal2 = 3, boolVal3 = true */ ... break; } 

据我所知,你不能用C#做到这一点。

但你可以从MSDN做到这一点:

以下示例显示允许空案例标签从一个案例标签到另一个案例标签:

  switch(n) { case 1: case 2: case 3: Console.WriteLine("It's 1, 2, or 3."); break; default: Console.WriteLine("Not sure what it is."); break; } 
 if (a == 1 && b == 1) {} else if (a == 1 && b == 2) {} else if (a == 2 && b ==2) {} 

我用列表或数组做这种事情。 如果您可以枚举可能的条件(如果您想要进行多值切换,您显然可以这样做),然后使用多部分键和ActionFunc作为值构建查找表。

一个简单的版本将使用一个Dictionary

 class LookupKey: IComparable { public int IntValue1 { get; private set; } public int IntValue2 { get; private set; } public bool BoolValue1 { get; private set; } public LookupKey(int i1, int i2, bool b1) { // assign values here } public int Compare(LookupKey k1, LookupKey k2) { return k1.IntValue1 == k2.IntValue1 && k1.IntValue2 == k2.IntValue2 && k1.BoolValue1 == k2.BoolValue1; } public int GetHashCode() { return (19 * IntValue1) + (1000003 * IntValue2) + (BoolValue1) ? 0 : 100000073; } // need to override Equals } 

而你的字典:

 static readonly Dictionary> LookupTable; 

然后,您可以在启动时填充字典,然后查找变得简单:

 Action MethodToCall; if (LookupTable.TryGetValue(new LookupKey(i1, i2, b1), out MethodToCall) MethodToCall(theData); else // default action if no match 

这是设置的一些代码,但它的执行速度非常快。

2018年更新。自C#7.0起,Microsoft为交换机引入了“when”子句,使其可以有效地扩展具有附加条件的交换机案例。

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch#the-case-statement-and-the-when-clause