Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# 8 switch expression for void methods

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?

like image 493
Shiva Naru Avatar asked May 26 '20 23:05

Shiva Naru


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.

What is C in C language?

What is C? C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.


3 Answers

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.

like image 67
Iliar Turdushev Avatar answered Oct 17 '22 13:10

Iliar Turdushev


As Iliar told you, it can't be done. A workaround that I use sometimes is to add a local function that always returns true and I use a discard char in the switch assignment. I find this walkaround a bit clearer than the one using delegates.

For example you would add this to your code before the switch:

bool Add() { [HEREYOURCODEToAdd]; return true;}
bool Substract() { [HEREYOURCODETOSubstract]; return true;}

And then you will call it, like this:

_ = stringvalue switch {
    "Add" => Add(),
    "Substract" => Substract(),
};

I think this approach has some use cases. For example if you want to use tuple patterns in the switch it would be much more readable than many annidated ifs. This is a code that I'm using in which I consider this approach better than ifs:

           bool agregar(int índiceInicial, int índiceFinal) { visitasJson.Add(json[índiceInicial..índiceFinal]); return true; };
            _ = (índiceInicial, índiceFinal) switch {
                (0, -1) => agregar(índiceInicial + 1, índiceFinal - 3),
                (_, -1) => agregar(índiceInicial, índiceFinal - 1),
                (0, _) => agregar(índiceInicial + 5, índiceFinal),
                (_, _) => agregar(índiceInicial, índiceFinal),
            };
like image 2
Desmond Avatar answered Oct 17 '22 13:10

Desmond


Do you really want to use switch expression in your use case, just traditional way is much better.

All proposed solution like delegates, discard are workarounds.

You are trying to wrongly use switch expression.

like image 1
Balamurugan.Santhanaraj Avatar answered Oct 17 '22 14:10

Balamurugan.Santhanaraj