C# 8 用于 void 方法的 switch 表达式

Shi*_*aru 22 c# switch-statement c#-8.0 switch-expression

我知道C# 8 switch expression返回值或属性匹配的方法的语法。但是如果我们只需要打开一个字符串值并执行一个不返回任何内容(没有返回类型/void)的方法,那么我们该怎么做呢?我正在考虑某种形式的 Func 但不确定确切的语法。我知道我们可以用常规的 case 语句用旧的方式来做,但试图看看我们是否可以用新的语法实现同样的效果。

这是问题的例子

switch (stringvalue)
{
    case "Add": Add(); break;
    case "Subtract": Subtract(); break;
}
Run Code Online (Sandbox Code Playgroud)

可能是这样的:

stringvalue switch
{
    "Add" => Add(),
    "Subtract" => Subtract()
}
// but this complains that there needs to be assignment on left side for this
Run Code Online (Sandbox Code Playgroud)

这可能吗?

Ili*_*hev 24

TL; 博士

这不可能。C# 8 switch expression无法返回void。它必须返回一个值并且必须使用该值(分配给变量、作为参数传递给方法、作为方法的结果返回等)。但是有一个解决方法。我们可以编写 aswitch expression返回 adelegateAction例如类型),然后立即调用它:

(stringValue switch
 {
    "Add" => (Action) Add,
    "Subtract" => Subtract,
    _ => throw new ArgumentOutOfRangeException()
 })();
Run Code Online (Sandbox Code Playgroud)

这种方法也可以与expression bodied methods. 这是演示


解释

这可能吗?

C# 8它是不可能的。此限制在 中描述C# specification

让我们参考C# specification。从网页Recursive Pattern Matching - Switch ExpressionStatements我们可以得知:

  • 所述switch_expression不允许作为expression_statement

  • Anexpression_statement计算给定的表达式。表达式计算的值(如果有)将被丢弃。

从这两个语句我们可以得出结论,switch_expression不能在 的上下文中使用expression_statement,并且它的结果值不能被丢弃。必须使用结果值,例如,必须将其分配给变量、作为参数传递给方法或作为方法的结果返回。因此编译器抱怨switch expression不能用作语句。


如果我们只需要打开一个字符串值并执行一个不返回任何内容(无返回类型/void)的方法,我们该怎么做?我正在考虑某种形式的 Func 但不确定确切的语法。

我们可以使用下一种方法:编写switch expression返回 a 的 adelegate然后立即调用它。例如:

(stringValue switch
 {
    "Add" => (Action) Add,
    "Subtract" => Subtract,
    _ => throw new ArgumentOutOfRangeException()
 })();
Run Code Online (Sandbox Code Playgroud)

这种方法也可用于声明expression bodied members

private static void Demo(string str) =>
    (str switch
     {
         "Add" => (Action) Add,
         "Subtract" => Subtract,
         _ => throw new ArgumentOutOfRangeException()
     })();
Run Code Online (Sandbox Code Playgroud)

这是完整的示例

在我看来,这种解决方法看起来很丑陋,我个人更喜欢使用switch-caseif-else代替这种结构。


可能在未来版本中C#会放宽此限制(请参阅此链接):

所述switch_expression不允许作为expression_statement

我们正在考虑在未来的修订中放宽这一点。

但是我在csharplang repo.

  • 不幸的是,这种解决方法是性能杀手。**不要**使用这个!每个“case”表达式都会遭受非常昂贵的[委托分配](https://devblogs.microsoft.com/pfxteam/know-thine-implicit-allocations/)(在.NET 5中验证)。 (7认同)

Des*_*ond 7

正如伊利亚尔告诉你的那样,这是不可能的。我有时使用的解决方法是添加一个始终返回 true 的本地函数,并在开关分配中使用丢弃字符。我发现这种解决方案比使用委托的解决方案更清晰一些。

\n

例如,您可以在切换之前将其添加到代码中:

\n
bool Add() { [HEREYOURCODEToAdd]; return true;}\nbool Substract() { [HEREYOURCODETOSubstract]; return true;}\n
Run Code Online (Sandbox Code Playgroud)\n

然后你会这样称呼它:

\n
_ = stringvalue switch {\n    "Add" => Add(),\n    "Substract" => Substract(),\n};\n
Run Code Online (Sandbox Code Playgroud)\n

我认为这种方法有一些用例。例如,如果您想在 switch 中使用元组模式,它将比许多已确定的 if 更具可读性。这是我正在使用的代码,我认为这种方法比 ifs 更好:

\n
           bool agregar(int \xc3\xadndiceInicial, int \xc3\xadndiceFinal) { visitasJson.Add(json[\xc3\xadndiceInicial..\xc3\xadndiceFinal]); return true; };\n            _ = (\xc3\xadndiceInicial, \xc3\xadndiceFinal) switch {\n                (0, -1) => agregar(\xc3\xadndiceInicial + 1, \xc3\xadndiceFinal - 3),\n                (_, -1) => agregar(\xc3\xadndiceInicial, \xc3\xadndiceFinal - 1),\n                (0, _) => agregar(\xc3\xadndiceInicial + 5, \xc3\xadndiceFinal),\n                (_, _) => agregar(\xc3\xadndiceInicial, \xc3\xadndiceFinal),\n            };\n
Run Code Online (Sandbox Code Playgroud)\n