Как сказано в названии, я хочу упростить оператор switch-case. В настоящее время у меня есть это в моем операторе switch-case:

switch(someEnum) {
case EnumType.A:
    SomeMethodSpecificToA();
    AMethodIShouldCallOnAllVowels();
    break;
case EnumType.B:
case EnumType.C:
case EnumType.D:
    SomeMethodSpecificToTheseThreeLetters();
    AMethodIShouldCallOnAllConsonants();
    break;
case EnumType.E:
    SomeMethodSpecificToE();
    AMethodIShouldCallOnAllVowels();
    break;
// All other letters, also containing the vowels & consonants methods
}

Итак, я знаю, что могу связать несколько операторов case, чтобы заставить их делать одно и то же, но я не знаю, как сделать так, чтобы две буквы выполняли две разные вещи, а затем переходили ко второму оператору, ибо все гласные (или все согласные). В Swift я бы сделал что-то вроде этого:

func test(someEnum: EnumType) {
    switch someEnum {
    case .A:
        someMethodSpecificToA()
        fallthrough
    case .B, .C, .D:
        someMethodSpecificToTheseThreeLetters()
        fallthrough
    case .E:
        someMethodSpecificToE()
        fallthrough
    case .A, .E:
        aMethodIShouldCallOnVowels()
    case .B, .C, .D:
        aMethodIShouldCallOnAllConsonants()
    }
}

Есть ли способ сделать это без использования двух операторов switch? Это кажется лишним, поскольку я уже включил эту переменную.

1
vrwim 3 Янв 2016 в 22:30

4 ответа

Лучший ответ

Я бы просто ограничил cases до Specific()'s, а после switch block поместил бы простое if-else:

if IsVowel
    AMethodIShouldCallOnAllVowels();
else
    AMethodIShouldCallOnAllConsonants();

Также ознакомьтесь с default: (но, вероятно, бесполезно Это дело).

4
Danny_ds 3 Янв 2016 в 19:42

Есть ли способ сделать это без использования двух операторов switch?

Да. Используйте операторы if.

EnumType[] Vowels = new [] {EnumType.A, EnumType.E, EnumType.I, EnumType.O, EnumType.U};

if (someEnum == EnumType.A)
    SomeMethodSpecificToA();

if (new [] {EnumType.B, EnumType.C, EnumType.D}.Contains(someEnum))
    SomeMethodSpecificToTheseThreeLetters();

if (someEnum == EnumType.E)
    SomeMethodSpecificToE();

if (Vowels.Contains(someEnum))
    AMethodIShouldCallOnAllVowels();

В зависимости от сложности «букв» в вашем реальном коде вы можете найти классы, а не перечисления, которые лучше подходят. При этом вы можете заменить всю условную логику (операторы if и switch). Один из вариантов рефакторинга может выглядеть примерно так:

abstract class Letter
{
    public char Value { get; private set; }
    protected abstract void FrobInternal();
    public void Frob()
    {
        FrobInternal();
        // optionally code to be called for all letters
    }

    // private constructor limits inheritance to nested classes
    private Letter(char value) { Value = value; }

    class Vowel : Letter
    {
        public Vowel(char letter) : base(letter) { }
        sealed protected override void FrobInternal()
        {
            FrobVowel();
            AMethodIShouldCallOnAllVowels();
        }
        protected virtual void FrobVowel() { }
        private void AMethodIShouldCallOnAllVowels()
        {
            // Implementation...
        }
    }
    class Consonant : Letter
    {
        public Consonant(char letter) : base(letter) { }
        sealed protected override void FrobInternal()
        {
            FrobConsonant();
            AMethodIShouldCallOnAllConsanants();
        }
        protected virtual void FrobConsonant() { }
        private void AMethodIShouldCallOnAllConsanants()
        {
            // Implementation...
        }
    }

    class ConsonantBCD : Consonant
    {
        public ConsonantBCD(char letter) : base(letter) { }
        protected override void FrobConsonant()
        {
            // Special implemenation for B, C, D
        }
    }

    class LetterA : Vowel
    {
        public LetterA() : base('A') { }
        protected override void FrobVowel()
        {
            // Special implementation for A
        }
    }
    class LetterE : Vowel
    {
        public LetterE() : base('E') { }
        protected override void FrobVowel()
        {
            // Special implementation for E
        }
    }

    // use public readonly fields to replicate Enum functionality
    public static readonly Letter A = new LetterA();
    public static readonly Letter B = new ConsonantBCD('B');
    public static readonly Letter C = new ConsonantBCD('C');
    public static readonly Letter D = new ConsonantBCD('D');
    public static readonly Letter E = new LetterE();
    public static readonly Letter F = new Consonant('F');
    // ...   
    public static readonly Letter Z = new Consonant('Z');
}

А затем вы можете просто заменить функцию-прототип выше:

void Test(Letter l) {
    l.Frob();
}

Вышеупомянутый рефакторинг - всего лишь один из вариантов закрытого набора значений для имитации перечисления. Также может быть полезно рассмотреть стратегию или шаблоны посетителей.

1
drf 3 Янв 2016 в 20:43

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

Действительно ли после этого рефакторинга вы действительно получаете более удобную в обслуживании программу? Разве решение KISS не заключалось бы в том, чтобы просто сгруппировать «все для А» после метки A?

0
xtofl 3 Янв 2016 в 19:42

Переход к следующему случаю - это «goto Case 2;». Я также считаю, что это связанный вопрос: [Использование клавиатуры `continue` в гнезде переключателя внутри цикла foreach

1
Community 23 Май 2017 в 11:44