Combine return and switch in C#
Asked Answered
M

15

73

How can I combine return and switch case statements in C#?

I want something like

return switch (a)
{
    case 1: "lalala"
    case 2: "blalbla"
    case 3: "lolollo"
    default: "default" 
};

I know about this solution

switch (a)
{
    case 1: return "lalala";
    case 2: return "blalbla";
    case 3: return "lolollo";
    default: return "default";
}

But I want to only use the return operator.

Magner answered 26/7, 2010 at 10:53 Comment(2)
AFAIK, switch in does not return a value, so your usage is impossible. And why do you want to do that?Rappee
Switch is now an expression and very useful, especially when combined with pattern matchingSanious
A
27

Note: As of C#8 (ten years later!) this is now possible, please see the answer below.


switch and return can't combine that way, because switch is a statement, not an expression (i.e., it doesn't return a value).
If you really want to use just a single return, you could make a Dictionary to map the switch variable to return values:

var map = new Dictionary<int, string>() 
{
    {1, "lala"}, 
    {2, "lolo"}, 
    {3, "haha"}, 
};
string output;
return map.TryGetValue(a, out output) ? output : "default";
Absence answered 26/7, 2010 at 11:6 Comment(1)
@Magner probably best to switch the Accepted answer to the other one!Absence
S
214

Actually this is possible using switch expression starting with C# 8.

return a switch
{
    1 => "lalala",
    2 => "blalbla",
    3 => "lolollo",
    _ => "default"
};

Switch Expression
There are several syntax improvements here:

  • The variable comes before the switch keyword. The different order makes it visually easy to distinguish the switch expression from the switch statement.
  • The case and : elements are replaced with =>. It's more concise and intuitive.
  • The default case is replaced with a _ discard.
  • The bodies are expressions, not statements.

For more information and examples check:

Salpingectomy answered 6/9, 2019 at 9:56 Comment(3)
I was waiting for this feature 10 yearsMagner
Stuff like this is why Rust is the most loved programming language n years in a row. This doesn't come close to Rust but is good enough though.Bowne
@Paul-SebastianManole Rust probably got the idea from Ruby :PDiaphoretic
A
27

Note: As of C#8 (ten years later!) this is now possible, please see the answer below.


switch and return can't combine that way, because switch is a statement, not an expression (i.e., it doesn't return a value).
If you really want to use just a single return, you could make a Dictionary to map the switch variable to return values:

var map = new Dictionary<int, string>() 
{
    {1, "lala"}, 
    {2, "lolo"}, 
    {3, "haha"}, 
};
string output;
return map.TryGetValue(a, out output) ? output : "default";
Absence answered 26/7, 2010 at 11:6 Comment(1)
@Magner probably best to switch the Accepted answer to the other one!Absence
A
22

I believe that this solution is the most straighforward one, and you should definitely use it:

switch(a) { 
  case 1: return "lalala"; 
  case 2: return "blabla"; 
  case 3: return "lololo"; 
  default: return "default"; 
} 

But, since you asked for one return, you could use this little fluent class:

public class Switch<TElement, TResult> {
  TElement _element;
  TElement _currentCase;
  IDictionary<TElement, TResult> _map = new Dictionary<TElement, TResult>();

  public Switch(TElement element) { _element = element; }
  public Switch<TElement, TResult> Case(TElement element) {
    _currentCase = element;
    return this;
  }
  public Switch<TElement, TResult> Then(TResult result) {
    _map.Add(_currentCase, result);
    return this;
  }
  public TResult Default(TResult defaultResult) {
    TResult result;
    if (_map.TryGetValue(_element, out result)) {
      return result;
    }
    return defaultResult;
  }
}

To create code like this:

  return new Switch<int, string>(a)
    .Case(1).Then("lalala")
    .Case(2).Then("blabla")
    .Case(3).Then("lololo")
    .Default("default");

Unfortunately, the type parameters could not be inferred by the compiler, and it feels a bit clumsy. The Default will trigger the evaluation of the "switch", and must be the last method call in the chain. Note that you always need a default value, since you've turned switch into an expression.

UPDATE: You can solve the type inference problem and drive the user to do the right thing with this code:

public static class Switch {

  public static SwitchBuilder<TElement>.CaseBuilder On<TElement>(TElement element) {
    return new SwitchBuilder<TElement>(element).Start();
  }

  public class SwitchBuilder<TElement> {
    TElement _element;
    TElement _firstCase;
    internal SwitchBuilder(TElement element) { _element = element; }
    internal CaseBuilder Start() {
      return new CaseBuilder() { Switch = this };
    }
    private ThenBuilder Case(TElement element) {
      _firstCase = element;
      return new ThenBuilder() { Switch = this };
    }
    private SwitchBuilder<TElement, TResult>.CaseBuilder Then<TResult>(TResult result) {
      return new SwitchBuilder<TElement, TResult>(
        _element,
        _firstCase,
        result).Start();
    }
    public class CaseBuilder {
      internal SwitchBuilder<TElement> Switch { get; set; }
      public ThenBuilder Case(TElement element) {
        return Switch.Case(element);
      }
    }
    public class ThenBuilder {
      internal SwitchBuilder<TElement> Switch { get; set; }
      public SwitchBuilder<TElement, TResult>.CaseBuilder Then<TResult>(TResult result) {
        return Switch.Then(result);
      }
    }
  }

  public class SwitchBuilder<TElement, TResult> {
    TElement _element;
    TElement _currentCase;
    IDictionary<TElement, TResult> _map = new Dictionary<TElement, TResult>();
    internal SwitchBuilder(TElement element, TElement firstCase, TResult firstResult) {
      _element = element;
      _map.Add(firstCase, firstResult);
    }
    internal CaseBuilder Start() {
      return new CaseBuilder() { Switch = this };
    }
    private ThenBuilder Case(TElement element) {
      _currentCase = element;
      return new ThenBuilder() { Switch = this };
    }
    private CaseBuilder Then(TResult result) {
      _map.Add(_currentCase, result);
      return new CaseBuilder() { Switch = this };
    }
    private TResult Default(TResult defaultResult) {
      TResult result;
      if (_map.TryGetValue(_element, out result)) {
        return result;
      }
      return defaultResult;
    }
    public class CaseBuilder {
      internal SwitchBuilder<TElement, TResult> Switch { get; set; }
      public ThenBuilder Case(TElement element) {
        return Switch.Case(element);
      }
      public TResult Default(TResult defaultResult) {
        return Switch.Default(defaultResult);
      }
    }
    public class ThenBuilder {
      internal SwitchBuilder<TElement, TResult> Switch { get; set; }
      public CaseBuilder Then(TResult result) {
        return Switch.Then(result);
      }
    }
  }

}

The result is this nice, type-safe, fluent interface; where at each step you'll only have the right choice of methods to call (e.g. Then after Case):

return Switch.On(a)
  .Case(1).Then("lalala")
  .Case(2).Then("blabla")
  .Case(3).Then("lololo")
  .Default("default");
Arian answered 26/7, 2010 at 12:46 Comment(4)
This is great, Jordao, but it would make more sense to have methods as parameters to the Thens (they could be anonymous). Like a normal case statement. You can have multiple lines of code in a case statement. So, you could say int x = b + c * d; return "The answer is " + a.ToString(); for example. Anyway, it would have to be a method that returns the same type of object that a (in the On clause) is. Make sense?Birthwort
@vbullinger: yes, it makes perfect sense. In fact, that's exactly what I started doing some time ago but didn't really finish.Ruzich
Mind-blowing construction. Deep generic and functional magic.Unrepair
A minor comment, the fluent expression is nice for smaller switch statements but for larger ones you'd lose the hashing functionality for speed that switch statements give. Although you probably wouldn't want to do a large switch in this method anyway :)Causative
P
17

I normally do it this way:

var result = null;

switch(a)
{
    case 1:
        result = "lalala";
        break;
    case 2:
        result = "blalbla";
        break;
    case 3:
        result = "lolollo";
        break;
    default:
        result = "default";
        break;
};

return result;
Paget answered 26/7, 2010 at 11:22 Comment(2)
And why is this superior to early-exit, which has less code and less variables?Maxson
This is NOT superior, but the question was to have only one return statement and that's the way to have only one.Paget
S
13

With the new C# 8, you can combine both return and switch. The new switch is so cute.

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand))
    };

The equivalent old switch is as below.

public static RGBColor FromRainbowClassic(Rainbow colorBand)
{
    switch (colorBand)
    {
        case Rainbow.Red:
            return new RGBColor(0xFF, 0x00, 0x00);
        case Rainbow.Orange:
            return new RGBColor(0xFF, 0x7F, 0x00);
        case Rainbow.Yellow:
            return new RGBColor(0xFF, 0xFF, 0x00);
        case Rainbow.Green:
            return new RGBColor(0x00, 0xFF, 0x00);
        case Rainbow.Blue:
            return new RGBColor(0x00, 0x00, 0xFF);
        case Rainbow.Indigo:
            return new RGBColor(0x4B, 0x00, 0x82);
        case Rainbow.Violet:
            return new RGBColor(0x94, 0x00, 0xD3);
        default:
            throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
    };
}

You can read about this feature here.

Spay answered 18/6, 2019 at 10:4 Comment(0)
A
6
switch(a)
{
    case 1: return "lalala";
    case 2: return "blalbla";
    case 3: return "lolollo";
    default: return "default";
}
Alcaraz answered 26/7, 2010 at 10:56 Comment(0)
B
6

This is the closest I can think of:

return    a==1 ? "lalala"
        : a==2 ? "blalbla"
        : a==3 ? "lolollo"
        : "default";
Brachial answered 8/9, 2010 at 20:19 Comment(0)
L
4

My mapping solution looks like Jordão's solution but it is more flexible and shorter.

return a
  .Map(1,"lalala")
  .Map(2,"blabla")
  .Map(3,"lololo")
  .Else(string.Empty);

Both arguments can also be a function:

return a
    .Map(x => x <= 0, "lalala")
    .Map(2, "blabla")
    .Map(x => x >= 3, x => "lololo" + x.ToString()); // lololo3 etc.
Lignite answered 22/7, 2013 at 18:41 Comment(0)
S
3

I've created a Nuget package (FluentSwitch) that should do what you want. So you can do the following:

var result = myValue.Switch()
    .When(1, "lalala")
    .When(2, "blalbla")
    .When(3, "lolollo")
    .Else("default")
    .Value();
Slimy answered 19/2, 2018 at 9:55 Comment(0)
R
3

As an extension to others' responses, i recommend using tuples together with return switch when a few parameters are involved in decision making. This combination with discard is pretty strong:

return (param1, param2, param3) switch
{
    (value1, value2, value3) => returnValue1,
    (value2, _, value3) => returnValue2
    (value3, _, _) => returnValue3

}

Another sample by Microsoft: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#tuple-patterns

Rechabite answered 17/8, 2021 at 11:1 Comment(0)
H
3

Using latest version of C# I have done with following way :

public string GetValue(string name)
        {
            return name switch
            {
                var x when name is "test1" || name is "test2" => "finch",
                "test2" => somevalue,
                _ => name
            };
        }
Hacker answered 17/10, 2022 at 19:53 Comment(0)
K
1

If you want switch to return value, you can use delegate:

int a = 2;
string result = new Func<string>(delegate ()
{
    switch (a)
    {
        case 1: return "lalala";
        case 2: return "blalbla";
        case 3: return "lolollo";
        default: return "default";
    }
})();

Or:

int a = 2;
string result = new Func<int,string>(delegate (int i)
{
    switch (i)
    {
        case 1: return "lalala";
        case 2: return "blalbla";
        case 3: return "lolollo";
        default: return "default";
    }
})(a);

Or just use lambda:

int a = 2;
string result = new Func<int,string>((int i) =>
{
    switch (i)
    {
        case 1: return "lalala";
        case 2: return "blalbla";
        case 3: return "lolollo";
        default: return "default";
    }
})(a);
Knowall answered 19/3, 2019 at 11:47 Comment(0)
F
0
public String doStaff(int a) {

   switch(a)
       {
          case 1: return "lalala"
          case 2: return "blalbla"
          case 3: return "lolollo"
          default: return "default" 
       };
}
Filemon answered 26/7, 2010 at 10:56 Comment(0)
N
0

We can have one use case where we may need to return the value from condition written inside the switch; let's say:

public void SomeMethod(SomeType enumType)  
{   
    switch (enumType)  
    {  
        case a:  
            if (condition)  
            {  
                if (condition1  && condition2)  
                {  
                    return true;  
                }  
            }  
            return false;  
            //break; break is actually not be required here if return used before break  
        case b:  
            if (condition)  
            {  
                if (condition3  && condition4)  
                {  
                    return true;  
                }  
            }  
            return false;  
            // break;  
        default:  
            return false;  
            //break;  
    }  

    Public enum SomeType  
    {  
        a,  
        b,  
        c,  
        d  
    }  
Neille answered 5/6, 2013 at 2:34 Comment(0)
U
-2

You can use (switch case) instead of (if statement).

    public InvoiceDeliveryStatus InvoiceDeliveryStatus { get; set; }

public string GetInvoiceDeliveryStatusTxt { get { return InvoiceDeliveryStatusSwitch(); } }


        private string InvoiceDeliveryStatusSwitch()
        {
           
            if (InvoiceDeliveryStatus == InvoiceDeliveryStatus.Canceled) return "Is Rejected";
            if (InvoiceDeliveryStatus == InvoiceDeliveryStatus.Completed) return "Is Completed";
       

                return InvoiceDeliveryStatus.ToString();
        }
Unpolite answered 16/5, 2022 at 12:40 Comment(1)
How does this answer the question? It doesn´t even contain the switch statement?Caltanissetta

© 2022 - 2024 — McMap. All rights reserved.