Объединить возврат и переключатель
Как я могу совместить return
а также switch case
заявления?
Я хочу что-то вроде
return switch(a)
{
case 1:"lalala"
case 2:"blalbla"
case 3:"lolollo"
default:"default"
};
Я знаю об этом решении
switch(a)
{
case 1: return "lalala";
case 2: return "blalbla";
case 3: return "lolollo";
default: return "default";
}
Но я хочу использовать только return
оператор.
15 ответов
switch
а также return
не может сочетаться таким образом, потому что switch
является оператором, а не выражением (т. е. не возвращает значение).
Если вы действительно хотите использовать только один return
Вы можете создать словарь, чтобы отобразить переменную-переключатель для возврата значений:
var map = new Dictionary<int, string>()
{
{1, "lala"},
{2, "lolo"},
{3, "haha"},
};
string output;
return map.TryGetValue(a, out output) ? output : "default";
На самом деле это возможно с помощью выражений переключателя, начиная с C# 8.
return a switch
{
1 => "lalala",
2 => "blalbla",
3 => "lolollo",
_ => "default"
};
Переключить выражения
Здесь есть несколько улучшений синтаксиса:
- Переменная стоит перед ключевым словом switch. Различный порядок позволяет визуально легко отличить выражение switch от оператора switch.
- Элементы case и: заменяются на =>. Это более лаконично и интуитивно понятно.
- Регистр по умолчанию заменяется на _ discard.
- Тела - это выражения, а не утверждения.
Дополнительные сведения и примеры см. В документе Microsoft C# 8 Whats New.
Я считаю, что это наиболее простое решение, и вы обязательно должны его использовать:
switch(a) {
case 1: return "lalala";
case 2: return "blabla";
case 3: return "lololo";
default: return "default";
}
Но, так как вы попросили один return
Вы могли бы использовать этот небольшой беглый класс:
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;
}
}
Чтобы создать такой код:
return new Switch<int, string>(a)
.Case(1).Then("lalala")
.Case(2).Then("blabla")
.Case(3).Then("lololo")
.Default("default");
К сожалению, параметры типа не могут быть определены компилятором, и это выглядит немного неуклюже. Default
вызовет оценку "switch" и должен быть последним вызовом метода в цепочке. Обратите внимание, что вам всегда нужно значение по умолчанию, так как вы повернули switch
в выражение.
ОБНОВЛЕНИЕ: Вы можете решить проблему вывода типов и заставить пользователя делать правильные вещи с этим кодом:
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);
}
}
}
}
Результатом является этот приятный, безопасный для типов, свободный интерфейс; где на каждом этапе у вас будет только правильный выбор методов для вызова (например, Then
после Case
):
return Switch.On(a)
.Case(1).Then("lalala")
.Case(2).Then("blabla")
.Case(3).Then("lololo")
.Default("default");
Я обычно делаю это так:
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;
С новым C# 8
, вы можете объединить оба return
а также switch
, Новый switch
это так мило.
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)),
};
Эквивалентный старый переключатель, как показано ниже.
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));
};
}
Вы можете прочитать об этой функции здесь.
Это самое близкое, о чем я могу подумать:
return a==1 ? "lalala"
: a==2 ? "blalbla"
: a==3 ? "lolollo"
: "default";
switch(a)
{
case 1: return "lalala";
case 2: return "blalbla";
case 3: return "lolollo";
default: return "default";
}
Мое картографическое решение выглядит как решение Jordão, но оно более гибкое и короче.
return a
.Map(1,"lalala")
.Map(2,"blabla")
.Map(3,"lololo")
.Else(string.Empty);
Оба аргумента также могут быть функцией:
return a
.Map(x => x <= 0, "lalala")
.Map(2, "blabla")
.Map(x => x >= 3, x => "lololo" + x.ToString()); // lololo3 etc.
Я создал пакет Nuget ( FluentSwitch), который должен делать то, что вы хотите. Таким образом, вы можете сделать следующее:
var result = myValue.Switch()
.When(1, "lalala")
.When(2, "blalbla")
.When(3, "lolollo")
.Else("default")
.Value();
В качестве дополнения к ответам других я рекомендую использовать кортежи вместе с переключателем возврата, когда несколько параметров участвуют в принятии решений. Эта комбинация со сбросом довольно сильна:
return (param1, param2, param3) switch
{
(value1, value2, value3) => returnValue1,
(value2, _, value3) => returnValue2
(value3, _, _) => returnValue3
}
Другой образец от Microsoft:https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#tuple-patterns
Используя последнюю версию С#, я сделал следующее:
public string GetValue(string name)
{
return name switch
{
var x when name is "test1" || name is "test2" => "finch",
"test2" => somevalue,
_ => name
};
}
Если вы хотите, чтобы switch возвращал значение, вы можете использовать делегат:
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";
}
})();
Или же:
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);
Или просто используйте лямбду:
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);
У нас может быть один случай использования, когда нам может понадобиться вернуть значение из условия, записанного внутри коммутатора; скажем так:
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
}
public String doStaff(int a) {
switch(a)
{
case 1: return "lalala"
case 2: return "blalbla"
case 3: return "lolollo"
default: return "default"
};
}
Вы можете использовать (переключить регистр) вместо (если оператор).
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();
}