void 方法的 C# 8 开关表达式

C# 8 switch expression for void methods(void 方法的 C# 8 开关表达式)

本文介绍了void 方法的 C# 8 开关表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这是问题的例子

开关(字符串值){案例添加":添加();休息;案例减":减();休息;}

可能是这样的:

字符串值开关{添加"=>添加(),减"=>减去()}//但这抱怨需要在左侧为此赋值

这可能吗?

解决方案

TL;DR

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

(stringValue 开关{添加"=>(行动)添加,减"=>减去,_ =>抛出新的 ArgumentOutOfRangeException()})();

这种方法也可以与表达式主体方法一起使用.这是演示.


说明

<块引用>

这可能吗?

C# 8 中这是不可能的.C# 规范中描述了此限制.

让我们参考C#规范.从页面 递归模式匹配 - 切换表达式Statements 我们可以了解到:

  • <块引用>

    switch_expression 不允许作为 expression_statement.

  • <块引用>

    expression_statement 计算给定的表达式.表达式计算的值(如果有)将被丢弃.

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


<块引用>

如果我们只需要打开一个字符串值并执行一个方法什么都不返回(没有返回类型/void),我们该怎么做呢?我是正在考虑某种形式的 Func,但不确定确切的语法.

我们可以使用下一种方法:编写一个返回 delegateswitch 表达式,然后立即调用它.例如:

(stringValue 开关{添加"=>(行动)添加,减"=>减去,_ =>抛出新的 ArgumentOutOfRangeException()})();

这个方法也可以用来声明表达式体成员:

private static void Demo(string str) =>(str开关{添加"=>(行动)添加,减"=>减去,_ =>抛出新的 ArgumentOutOfRangeException()})();

这里是完整示例.

在我看来,这样的解决方法看起来很丑陋,我个人更喜欢使用 switch-caseif-else 而不是这样的构造.

感谢l33t的评论.他指出,使用这种方法会导致每个case expression遭受非常昂贵的委托分配(在 .NET 5 中验证).所以还有一个理由不使用这种方法,而是更喜欢使用 switch-caseif-else.


可能会在未来版本的 C# 中放宽此限制(查看此链接):

<块引用>

switch_expression 不允许作为 expression_statement.

<块引用>

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

但我在 cshaplang repo.

I'm aware of the C# 8 switch expression syntax for methods that return a value or for property matching. But if we just need to switch on a string value and execute a method that returns nothing (no return type/void), then how do we go about it? I am thinking some form of Func but not sure of the exact syntax. I know we can do it the old way with regular case statements but was trying to see if we can achieve the same with the newer syntax.

Here's the example of the problem

switch (stringvalue)
{
    case "Add": Add(); break;
    case "Subtract": Subtract(); break;
}

could be like:

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

Is this possible?

解决方案

TL;DR

It is not possible. In C# 8 switch expression cannot return void. It must return a value and this value must be consumed (assigned to a variable, passed as an argument to a method, returned as a result of a method, etc.). But there is a workaround. We can write a switch expression that returns a delegate (of type Action, for example) and then immediately invokes it:

(stringValue switch
 {
    "Add" => (Action) Add,
    "Subtract" => Subtract,
    _ => throw new ArgumentOutOfRangeException()
 })();

Such approach also can be used with expression bodied methods. Here is demo.


Explanation

Is this possible?

In C# 8 it is not possible. This limitation is described in C# specification.

Lets refer to C# specification. From pages Recursive Pattern Matching - Switch Expression and Statements we can learn that:

  • The switch_expression is not permitted as an expression_statement.

  • An expression_statement evaluates a given expression. The value computed by the expression, if any, is discarded.

From these two statements we can conclude that switch_expression cannot be used in the context of expression_statement, and its result value cannot be discarded. The result value must be used, for example, it must be assigned to a variable, passed to a method as an argument or returned as a result of a method. Therefore compiler complains that switch expression cannot be used as a statement.


if we just need to switch on a string value and execute a method that returns nothing (no return type/void), how do we go about it? I am thinking some form of Func but not sure of the exact syntax.

We can use the next approach: write a switch expression that returns a delegate and then immediately invokes it. For example:

(stringValue switch
 {
    "Add" => (Action) Add,
    "Subtract" => Subtract,
    _ => throw new ArgumentOutOfRangeException()
 })();

This approarch also can be used to declare expression bodied members:

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

Here is complete sample.

In my opinion such workaround looks ugly and personally I would prefer to use switch-case or if-else instead of such construction.

Thanks to l33t's comment. He pointed out that using such approach would cause each case expression suffer from very costly delegate allocation (verified in .NET 5). So it is one more reason not to use this approach and prefer to use switch-case or if-else.


May be in future version of C# this limitation will be relaxed (see this link):

The switch_expression is not permitted as an expression_statement.

We are looking at relaxing this in a future revision.

But I haven't found an appropriate proposal in csharplang repo.

这篇关于void 方法的 C# 8 开关表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:void 方法的 C# 8 开关表达式

基础教程推荐