48

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

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

   etc ....
}

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

.net 开发团队通过实现这个恐惧来回答这个问题:Multi-variable switch statement in C#

4

13 回答 13

47

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

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;
}
于 2018-05-26T14:37:59.963 回答
44

是的。从 .NET 4.7 和 C# 8 开始支持它。语法与您提到的差不多,但带有一些括号(请参阅 tuple patterns)。

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

如果您想切换并返回一个值,则有一个切换“表达式语法”。这是一个例子;注意_默认情况下的使用:

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

这是上面链接的 MSDN 文章中的一个更具说明性的示例(石头、纸、剪刀游戏):

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"
    };
于 2019-10-01T19:25:37.793 回答
13

在 C# 中没有(曾经)执行此操作的内置功能,而且我不知道有任何库可以执行此操作。

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

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

这基本上只是提供了一种方便的语法来检查多个值 - 并使用多个ifs 而不是switch.

于 2011-11-01T14:03:03.907 回答
12

让我们以另一种方式来看待这个问题。如果你有:

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

然后您可以改用查找表,它的执行速度与switch语句相似,但效率不高(因为它需要计算哈希)。不过,它可能已经足够好了。它使您有机会命名案例,使这种组合爆炸稍微不那么令人困惑和难以维护。

代码示例:

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

如果您需要为每种情况实际执行一个函数或方法,那么您可以使用Action<T>or的结果类型(字典值) Func<T>

请注意,我在Tuple<T1,T2,T3>这里使用它是因为它已经内置了所有哈希码逻辑。C# 中的语法有点笨拙,但如果您愿意,您可以实现自己的查找类并覆盖Equalsand GetHashCode

于 2011-11-01T14:20:20.740 回答
6

我对此非常疯狂:

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<T1, T2, T3> Match<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
    {
        return new Matcher<T1, T2, T3>(t1, t2, t3);
    }
}

public class Matcher<T1, T2, T3>
{
    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<T1, T2, T3> With(T1 t1, T2 t2, T3 t3)
    {
        return new Match<T1, T2, T3>(this, new object[] { t1, t2, t3 });
    }

    public Match<T1, T2, T3> With(Func<T1, bool> t1, Func<T2, bool> t2, Func<T3, bool> t3)
    {
        return new Match<T1, T2, T3>(this, t1, t2, t3);
    }
}

public class Match<T1, T2, T3>
{
    private readonly Matcher<T1, T2, T3> matcher;
    private readonly object[] matchedValues;
    private readonly Func<object[], bool> matcherF; 

    public Match(Matcher<T1, T2, T3> matcher, object[] matchedValues)
    {
        this.matcher = matcher;
        this.matchedValues = matchedValues;
    }

    public Match(Matcher<T1, T2, T3> matcher, Func<T1, bool> t1, Func<T2, bool> t2, Func<T3, bool> t3)
    {
        this.matcher = matcher;


        matcherF = objects => t1((T1)objects[0]) && t2((T2)objects[1]) && t3((T3)objects[2]);
    }

    public Matcher<T1, T2, T3> Do(Action a)
    {
        if(matcherF != null && matcherF(matcher.Values) || matcher.Values.SequenceEqual(matchedValues))
            a();

        return matcher;
    }
}
于 2011-11-01T14:07:46.417 回答
6

您可以转换为字符串:

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 的额外属性,它可以是枚举或字符串,您可以使用它来指定某人所在的组。

于 2011-11-01T14:48:12.293 回答
2

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

于 2018-05-25T18:29:17.740 回答
2

我不确定它出现的是哪个 C# 版本,但你可以这样做:

var x = 22;
var y = 33;
var z = 44;

switch (x, y, z) {

    case (33, 33, 33):
        WriteLine("This should not run");
        break;

    case (22, 33, 44):
        WriteLine("This should run");
        break;
}
于 2021-09-12T01:28:20.337 回答
1

根据 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;
}
于 2011-11-01T14:02:33.597 回答
0

据我所知,你不能在 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; 
        }
于 2011-11-01T13:57:17.157 回答
0
if (a == 1 && b == 1) {}
else if (a == 1 && b == 2) {}
else if (a == 2 && b ==2) {}
于 2011-11-01T14:00:40.600 回答
0

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

一个简单的版本将使用Dictionary

class LookupKey: IComparable<LookupKey>
{
    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<LookupKey, Action<object>> LookupTable;

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

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

需要设置一些代码,但执行速度非常快。

于 2011-11-01T14:22:28.743 回答
0
//.Net Core 3.1
    class Convertors
    {
        static void Main(string[] args)
        {
            Console.WriteLine(Convertors.ConvertAny("m","cm", 10));
            Console.ReadKey();
        }
        public static double MToCM(double value)
        {
            return value * 100;
        }
        public static double ConvertAny(string srcUOM, string tgtUOM, double value)
        {
            switch (srcUOM.ToLower(), tgtUOM.ToLower())
            {
                case ("m", "cm"): return Convertors.MToCM(value);
                default: throw new NotImplementedException();
            }
        }
    }
于 2020-01-13T13:03:15.923 回答