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",
};

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

7 ответов

Решение

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

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

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",
};

Пока не появится стандартный синтаксис, я использую следующий метод расширения:

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).

К сожалению, это недостаток синтаксиса выражения-переключателя по сравнению с синтаксисом оператора-переключателя. Как предполагали другие плакаты, довольно неуклюжий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 Type3 => 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;

C#9 решает эту проблему с несколькими случаями:

          var result = foo switch
    {
        1 => ...,
        2 or 3 => ...,
        _ => throw ...
    };

Если вы все еще используете C # 8 и не можете использовать C # 9 1 or 2 or 3 => метод, как в ответе выше, вы также можете написать его таким образом, чтобы избежать создания дополнительной переменной с тем же значением, что и switchValue как в принятом ответе (который, конечно, совершенно правильный, я просто предлагаю альтернативу, поскольку мне не нравится этот дополнительный x).

      var list = new List<int> { 3, 4, 5 };

var resultText = switchValue switch
{
    1 => "one",
    2 => "two",
    _ when list.Contains(switchValue) => "three to five",
    _ => "unknown",
};

Очевидно, вы можете заменить условие после символа на что угодно, например switchValue == 3 || switchValue == 4 || switchValue == 5; суть ответа - показать _ when немного.

Я просто думаю о _ when => что означает "иначе, когда" и _ => что означает «еще».

Я сделал таким образом:

      public string GetValue(string name)
            {
                return name switch
                {
                    var x when name is "test1" || name is "test2" => "finch",
                    "test2" => somevalue,
                    _ => name
                };
            }

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

[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",
};
Другие вопросы по тегам