Выражение переключателя C # 8 с несколькими вариантами с одинаковым результатом

Как можно написать выражение переключения для поддержки нескольких случаев, возвращающих один и тот же результат?

В C # до версии 8 переключатель можно записать так:

var switchValue = 3;
var resultText = string.Empty;
switch (switchValue)
{
    case 1:
    case 2:
    case 3:
        resultText = "one to three";
        break;
    case 4:
        resultText = "four";
        break;
    case 5:
        resultText = "five";
        break;
    default:
        resultText = "unkown";
        break;
}

Когда я использую C # версии 8 с синтаксисом выражения, это выглядит так:

var switchValue = 3;
var resultText = switchValue switch
{
    1 => "one to three",
    2 => "one to three",
    3 => "one to three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};

Итак, мой вопрос: как превратить случаи 1, 2 и 3 только в одно плечо переключателя, чтобы значение не нужно было повторять?

Обновление по предложению от Руфуса Л.:

В моем примере это работает.

var switchValue = 3;
var resultText = switchValue switch
{
    var x when (x >= 1 && x <= 3) => "one to three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};

Но это не совсем то, чего я хочу достичь. Это по-прежнему только один случай (с условием фильтрации), а не несколько случаев, дающих один и тот же результат справа.


person huzle    schedule 19.06.2019    source источник
comment
Чего делать вы хотите достичь? Выражения переключения не являются операторами переключения, и провал явно запрещен. when в любом случае намного мощнее, чем провал. Вы можете использовать Contains с массивом значений, если хотите.   -  person Panagiotis Kanavos    schedule 20.06.2019
comment
Вместо использования 3, 4 или 11 операторов case вы можете использовать single var x when listOfValues.Contains(x) и обрабатывать столько случаев, сколько захотите.   -  person Panagiotis Kanavos    schedule 20.06.2019
comment
Я не пытаюсь провалить здесь дела. Как показано в первом блоке кода моего вопроса, 1,2 и 3 выполняют один и тот же код правой руки. И для ответа на этот вопрос я привел очень простой пример. Представьте себе, что в случае 1,2,3 будут оцениваться очень разные и сложные вещи, такие как сопоставление шаблонов с «когда» и т. Д.   -  person huzle    schedule 20.06.2019
comment
первый блок кода не выдерживает случаев. Вот что делают case 1: case 2: - это кейсы с пустыми блоками, которые переходят в следующий   -  person Panagiotis Kanavos    schedule 20.06.2019
comment
Что касается сложных вещей, when может справиться с гораздо более сложными случаями, чем простой провал. Простой провал может обрабатывать только проверки равенства по жестко запрограммированному значению. Сопоставление с образцом и when в выражениях переключателя одинаковы. Фактически, именно операторы switch вызывают проблемы с сопоставлением с образцом. Если вы проверите функциональные языки например F #, вы увидите, что основной вариант использования (часто только один) - это выражения сопоставления с образцом.   -  person Panagiotis Kanavos    schedule 20.06.2019
comment
Под провалом я имел в виду непустые тела на ящиках.   -  person huzle    schedule 20.06.2019
comment
Поэтому я думаю, что правильный способ ответить на мой вопрос - это использовать выражение в предложении «когда».   -  person huzle    schedule 20.06.2019


Ответы (4)


Я дошел до его установки, но не нашел способа указать несколько отдельных меток case для одного раздела переключателя с новым синтаксисом.

Однако вы можете создать новую переменную, которая фиксирует значение, а затем использовать условие для представления случаев, которые должны иметь тот же результат:

var resultText = switchValue switch
{
    var x when
        x == 1 ||
        x == 2 ||
        x == 3 => "one to three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};

На самом деле это более кратко, если у вас есть много случаев для тестирования, потому что вы можете протестировать диапазон значений в одной строке:

var resultText = switchValue switch
{
    var x when x > 0 && x < 4 => "one to three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};
person Rufus L    schedule 19.06.2019
comment
Падение категорически запрещено. Предложение when намного мощнее, чем все равно провалиться. Вместо того, чтобы писать 3 или 10 case операторов, вы можете использовать один var x when listOfValues.Contains(x) - person Panagiotis Kanavos; 20.06.2019
comment
Поэтому я думаю, что правильный способ ответить на мой вопрос - это использовать выражение в предложении «когда». Поэтому я отмечаю ваш ответ как правильный, потому что он соответствует требованиям. В частности, первый блок кода отформатирован так, чтобы в каждой строке был один регистр. - person huzle; 20.06.2019
comment
@PanagiotisKanavos Я только что узнал, что, хотя Fall Through запрещен, это не Fall Through. Это кратное сравнение, которым разрешено и компилирует: sharplab.io/ - person Christopher; 14.12.2019
comment
@Christopher то, на что вы ссылаетесь, проваливается в операторах switch. Был задан вопрос, как сделать то же самое с выражениями переключения, что запрещено. - person Panagiotis Kanavos; 16.12.2019
comment
@PanagiotisKanavos За исключением того, что это не провал, о чем я и говорил. Выполняется только один раздел switch в операторе switch. C # не позволяет продолжить выполнение от одного раздела переключения к другому. Из-за этого следующий код генерирует ошибку компилятора CS0163: Control не может переходить от одной метки case (‹case label›) к другой. - docs.microsoft.com/en-us/dotnet / csharp / language-reference / | Это было совпадение по нескольким меткам. Скорее, чем список, разделенный запятыми, как в Python. - person Christopher; 16.12.2019
comment
@PanagiotisKanavos Что может вас смутить, так это то, что во многих Langauge реализовано множественное математическое вычисление через Fall Through. Но в C # они такие же разные, как делегаты и целые числа. Или интерфейсы и классы. - person Christopher; 16.12.2019
comment
@Christopher Вы утверждаете, что я должен перефразировать провал на что-то другое? Я назвал использование нескольких операторов case в разделе switch провалом (как и @PanagiotisKanavos), но был бы рад использовать другой термин, если это сбивает с толку. - person Rufus L; 16.12.2019
comment
@RufusL Я действительно не уверен, что это правильный термин. Все, что я знаю наверняка, это то, что это не провал. На самом деле я сам так думал, поскольку помнил, как это реализовывалось с помощью Falltrhough, когда я только начинал работать с Native C ++. Но парень, который меня поправил, был прав: это не провал. Это не может быть провалом. Это что-то другое. И я не уверен, является ли предложение when просто подмножеством для сопоставления с образцом или вообще третьим способом. - person Christopher; 16.12.2019
comment
Справедливо. Я удалил термин и просто заменил его несколькими словами. :) - person Rufus L; 16.12.2019
comment
Похоже, они немного поправят это с помощью C # 9 улучшенное сопоставление с образцом. - person Ryan Sparks; 12.06.2020
comment
Скомпилированный результат очень похож независимо от используемого синтаксиса shorturl.at/gzCO5 - person Sz. Moncz; 18.12.2020

C # 9 поддерживает следующее:

var switchValue = 3;
var resultText = switchValue switch
{
    1 or 2 or 3 => "one, two, or three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};

Альтернативно:

var switchValue = 3;
var resultText = switchValue switch
{
    >= 1 and <= 3 => "one, two, or three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};

Источник


Для более старых версий C # я использую следующий метод расширения:

public static bool In<T>(this T val, params T[] vals) => vals.Contains(val);

нравится:

var switchValue = 3;
var resultText = switchValue switch
{
    var x when x.In(1, 2, 3) => "one, two, or three",
    4 => "four",
    5 => "five",
    _ => "unknown",
};

Он немного более краток, чем when x == 1 || x == 2 || x == 3, и имеет более естественный порядок, чем when new [] {1, 2, 3}.Contains(x).

person Zev Spitz    schedule 13.05.2020
comment
Он также выделяет новый временный массив Int, который вызовет давление сборщика мусора, если он находится внутри цикла, и примерно в 100 раз медленнее, чем 1 || 2 - но это, вероятно, не имеет значения для большей части кода - person Orion Edwards; 28.07.2020

К сожалению, это кажется недостатком в синтаксисе выражения-переключателя по сравнению с синтаксисом оператора-переключателя. Как предлагали другие авторы, довольно неуклюжий синтаксис var - ваш единственный реальный вариант.

Итак, вы, возможно, надеялись написать:

switchValue switch {
    Type1 t1:
    Type2 t2:
    Type3 t3 => ResultA, // where the ResultX variables are placeholders for expressions.
    Type4 t4 => ResultB,
    Type5 t5 => ResultC
};

Вместо этого вам нужно будет написать довольно неудобный код ниже с указанием typename:

switchValue switch {
    var x when x is Type1 || x is Type2 || x is Type 3 => ResultA,
    Type4 t4 => ResultB,
    Type5 t5 => ResultC
};

В таком простом примере вы, вероятно, сможете жить с этой неловкостью. Но с более сложным примером жить гораздо труднее. На самом деле мои примеры на самом деле являются упрощением примера, взятого из нашей собственной базы кода, где я надеялся преобразовать оператор-переключатель с примерно шестью результатами, но более чем с дюжиной вариантов типов, в выражение-переключатель. И результат был явно менее читабельным, чем оператор switch.

Я считаю, что если выражение-переключатель требует общих результатов и имеет длину более нескольких строк, тогда вам лучше придерживаться оператора-переключателя. Бу! Это более многословно, но, вероятно, из доброты к вашим товарищам по команде.

ResultType tmp;
switch (switchValue) {
    case Type1 t1:
    case Type2 t2:
    case Type3 t3:
        tmp = ResultA;
        break;
    case Type4 t4:
        tmp = ResultB;
        break;
    case Type5 t5:
        tmp = ResultC;
        break;
};
return tmp;
person sfkleach    schedule 20.10.2019
comment
Я знаю, что это всего лишь пример, но его можно немного очистить, напрямую вернувшись из кейсов, а не задав tmp. Таким образом, вы также можете опустить breaks. - person Ben; 13.11.2019
comment
Комментарий @Ben справедлив и, при прочих равных, мой собственный стиль. Но я решил не изменять свой ответ, потому что на самом деле в исходном посте не было возврата, и я хотел, чтобы вычисления и возврат были четко разделены, чтобы рефакторинг был более ясным. - person sfkleach; 07.01.2020
comment
Поскольку вы не используете совпадающие переменные, я бы рекомендовал обновить пример оператора switch, чтобы использовать шаблон отбрасывания для случаев: case Type1 _:, case Type2 _: и т. Д. - person julealgon; 29.01.2020
comment
@julealgon да, использование отбрасывания - лучший стиль. В этом случае я использовал имена переменных, потому что ResultA, ResultB, ResultC предназначены для использования в качестве заполнителей для выражений, которые могут ссылаться на t1, t2 ... t5. Я добавил комментарий, чтобы прояснить это. - person sfkleach; 30.01.2020

Если ваш тип переключателя - перечисление флагов

[System.Flags]
public enum Values 
{
    One = 1, 
    Two = 2, 
    Three = 4,
    Four = 8,
    OneToThree = One | Two | Three
}

var resultText = switchValue switch
{
    var x when Values.OneToThree.HasFlag(x) => "one to three",
    Values.Four => "4",
    _ => "unknown",
};
person pcchan    schedule 08.01.2020