Combine return and switch
Solution 1
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";
Solution 2
Actually this is possible using switch expressions starting with C# 8.
return a switch
{
1 => "lalala",
2 => "blalbla",
3 => "lolollo",
_ => "default"
};
Switch Expressions
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 the Microsoft's C# 8 Whats New.
Solution 3
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");
Solution 4
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;
Solution 5
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.
Neir0
Updated on August 18, 2021Comments
-
Neir0 almost 3 years
How can I combine
return
andswitch case
statements?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. -
Konrad Rudolph almost 14 yearsAnd why is this superior to early-exit, which has less code and less variables?
-
Oliver almost 14 yearsThis is NOT superior, but the question was to have only one return statement and that's the way to have only one.
-
vbullinger almost 12 yearsThis 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?
-
Jordão almost 12 years@vbullinger: yes, it makes perfect sense. In fact, that's exactly what I started doing some time ago but didn't really finish.
-
SerG over 6 yearsMind-blowing construction. Deep generic and functional magic.
-
Neir0 over 4 yearsI was waiting for this feature 10 years
-
robs almost 4 yearsA 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 :)
-
Paul-Sebastian over 3 yearsStuff 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.