Переключити перехід заяви на випадок ... чи слід це дозволити? [зачинено]


120

До тих пір, як я пам'ятаю, я уникав використання переключення оператора перемикання. Насправді я не можу згадати, щоб він коли-небудь входив у мою свідомість як можливий спосіб робити речі, оскільки це було просвердлено мені в голову рано, що це було не що інше, як помилка в заяві перемикача. Однак сьогодні я зіткнувся з деяким кодом, який використовує його при розробці дизайну, і мене одразу задало питання, що всі в громаді думають про пропуск транзакції.

Це щось, що мова програмування явно не повинна дозволити (як, наприклад, C #, хоча вона забезпечує вирішення) або це особливість будь-якої мови, достатньо потужної, щоб залишити її в руках програміста?

Редагувати: Я не був достатньо конкретним щодо того, що я мав на увазі під пропадом. Я багато використовую цей тип:

    switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

Однак мене турбує щось подібне.

   switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something, but fall through to the other cases
            // after doing it.

        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

Таким чином, коли випадок дорівнює 0, 1, він буде робити все в операторі комутатора. Я бачив це за допомогою дизайну, і я просто не знаю, чи згоден я, що заяви переключення повинні використовуватися таким чином. Я думаю, що перший приклад коду дуже корисний і безпечний. Друга видається небезпечною.



51
Не згоден з приводу неконструктивного. 27 кандидатів, які звернулися до цього питання, стверджують, що є й інші, які так почуваються.
Режими Уеса

2
"не конструктивна" - стара і дещо невиразна близька причина. Сьогодні такі питання можуть бути і повинні бути відмічені для закриття як суто обґрунтовані думкою. Отож, для відповідей на питання щодо коду чи дизайну, а не про те, якою є думка "громади" [що б там не було] про мою думку про Feature X ". Programmers.SE було б краще підходити, але навіть тоді значення такого широкого та орієнтованого на думку питання все ще залишається дуже сумнівним.
підкреслюйте_d

1
Існують дуже чіткі випадки використання, коли використовується перехід, тому не впевнений, чому це було б на думці. Я погоджуюся, що це питання дещо незрозуміле, але воно не повинно було бути закритим як не конструктивне. Можливо, щойно відредагований буде більш чітким випадком: "Коли використовується перехід"? Що має чітку відповідь. Хороші тексти на C ++ перебігають це, це заплутано для нових кодерів (або навіть більш досвідчених кодерів з інших мов), тому здається, що питання хороший для SO. Дійсно, це здається прототипно гарним питанням SO. Навіть якби шлях про це не був ідеальним.
eric

Відповіді:


89

Це може залежати від того, що ви вважаєте переломним. Я все в порядку з такими речами:

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

Але якщо у вас є мітка справи з кодом, який потрапляє на інший регістр, я завжди завжди вважаю це злом. Можливо, краще перенести загальний код на функцію та зателефонувати з обох місць.

І будь ласка, зауважте, що я використовую визначення C ++ поширених питань "зла"


Я згоден з вами: я не вважаю ваш приклад як проривним, і якщо є випадок, коли я хочу виконати кілька блоків коду через провал, мабуть, є кращий спосіб зробити це.
Дейв ДюПлантіс

10
+1 просто для цього визначення "зла". Я позичу це, спасибі ;-)
Йоахім Зауер

Я розумію, і ти маєш рацію, але твій приклад справді поганий. Ніхто не повинен перевіряти такі тестові цифри. Scnr, ваша відповідь все одно правильна.
Тім Б’юте

Справа в суті, це саме те, що C # забороняє. Ймовірно, з причини :-)
Joey

На жаль, визначення зла, схоже, відійшло в офлайн
сподіваємось,

57

Це меч з двома кінцями. Іноді це дуже корисно, але часто небезпечно.

Коли це добре? Коли ви хочете, щоб 10 справ оброблялися однаково ...

switch (c) {
  case 1:
  case 2:
            ... Do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... Do something ...
            break;
  case 5:
  case 43:
            ... Do something else ...
            break;
}

Єдине правило, яке мені подобається, це те, що якщо ви коли-небудь займаєтесь фантазією, коли ви виключаєте перерву, вам потрібен чіткий коментар / * ПІДТРИМКА * /, щоб вказати, що це був ваш намір.


3
+1! Я погоджуюсь, що це час від часу правильна відповідь, і я погоджуюсь, що коли це задумано, це слід коментувати. (У огляді коду я б змусив інший розробник прокоментувати непрозорий пропуск дизайну. Не те, що трапляється; ми - магазин C #, де це не підтримується :)
Джон Руді

4
Я сам використовую / * FALL THROHH * / коментар у своєму коді, де це доречно. =]
страгер

2
Якщо ви використовуєте PC-Lint, вам доведеться вставити / * - прорив * /, інакше він скаржиться.
Стів Мельников

1
все ж, якщо ви хотіли дати зрозуміти, що поведінка навмисна, ви також можете if(condition > 1 && condition <10) {condition = 1}; switch(...заощадити випадки (для чіткого вигляду), а всі можуть побачити, що ви дійсно хотіли, щоб ці випадки викликали ту саму дію.
Марк

1
Це все ще страшне кодування. Перший випадок повинен викликати функцію, другий випадок повинен викликати ту саму функцію, потім викликати другу.
BlueRaja - Danny Pflughoeft

21

Ви чули про пристрій Даффа ? Це чудовий приклад використання перемикача комутатора.

Це функція, якою можна користуватися, і нею можна зловживати, як і майже всі мовні функції.


Щодня дізнайтеся щось нове - спасибі! Я відчуваю себе бідними дияволами, які йдуть, хоча й такі спотворення.
Джастін Р.

Нічого собі, це зовсім щось, щоб обернути ваш мозок.
Фоста

6
Зауважте, цитуючи коментар Даффа в цій статті: "Цей код формує якийсь аргумент у цій дискусії, але я не впевнений, чи" за ", чи" проти ".
Джон Картер

Пристрій Даффа явно злий
Marjeta

21

Проникнення - це справді зручна річ, залежно від того, що ти робиш. Розглянемо цей акуратний і зрозумілий спосіб впорядкувати варіанти:

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // Do something
    break;

  case 'd':
  case 'e':
    // Do something else
    break;
}

Уявіть, що ви робите це за допомогою if / else. Це був би безлад.


1
Мені здається, що цей тип падіння є дуже корисним
Mitchel Sellers

якщо "зробити щось" більше, ніж просто трохи, ніж перемикач, швидше буде безлад, ніж якщо / ще. З if / else скрізь зрозуміло, яка змінна тестується. Навіть цей маленький приклад не виглядав би так погано, якби в моїх очах
grenix

10

Це може бути дуже корисним кілька разів, але в цілому жодне проникнення не є бажаною поведінкою. Пропуск повинен бути дозволений, але не неявний.

Приклад оновлення старих версій деяких даних:

switch (version) {
    case 1:
        // Update some stuff
    case 2:
        // Update more stuff
    case 3:
        // Update even more stuff
    case 4:
        // And so on
}

6

Я б хотів, щоб інший синтаксис для резервних копій в комутаторах, щось на кшталт, помилка ..

switch(myParam)
{
  case 0 or 1 or 2:
    // Do something;
    break;
  case 3 or 4:
    // Do something else;
    break;
}

Примітка. Це може бути можливим із перерахунками, якщо ви задекларуєте всі випадки вашого переліку за допомогою прапорів, правда? Це теж не так погано звучить; справи можуть (повинні?) дуже вже входити до вашого переліку.

Можливо, це був би приємний випадок (не призначений каламбур) для вільного інтерфейсу з використанням методів розширення? Щось на кшталт помилки ...

int value = 10;
value.Switch()
  .Case(() => { /* Do something; */ }, new {0, 1, 2})
  .Case(() => { /* Do something else */ } new {3, 4})
  .Default(() => { /* Do the default case; */ });

Хоча це, мабуть, ще й менш читабельно: P


1
Перша пропозиція мені дуже подобається. Це дуже добре читає і зберігає кілька рядків коду. Друга знадобилася хвилина, щоб обернути мій мозок, але має сенс, але це виглядає затопленим, щоб бути безладом.
Фоста

1
Так, я теж думав, що я просто придушив випадковий розум, намагаючись знайти інший спосіб переключення за допомогою існуючих мовних конструкцій.
Ерік ван Бракель

Пробігся через це питання, і мені це справді подобається! У мене була ідея, як поліпшити читабельність цього, але ТА не дозволяє мені публікувати багаторядкові коментарі :( Хтось повинен реалізувати це так само, як POC, цікаво реалізувати та використовувати (:
Віктор Еліас

5

Як і будь-що: якщо використовувати його обережно, це може бути елегантним інструментом.

Однак, я вважаю, що недоліки більш ніж виправдовують його не використовувати і, нарешті, більше не дозволяти (C #). Серед проблем:

  • легко «забути» перерву
  • не завжди очевидно, що для тех, хто підтримує код , пропущений розрив був навмисним

Добре використовувати перемикач / випадок корпусу:

switch (x)
{
case 1:
case 2:
case 3:
 Do something
 break;
}

Використання перемикача / корпусу, що проходить через Baaaaad :

switch (x)
{
case 1:
    Some code
case 2:
    Some more code
case 3:
    Even more code
    break;
}

Це можна переписати, використовуючи if / else конструкції без втрат на мою думку.

Моє остаточне слово: тримайтеся подалі від міток справи, що пропускаються, як у поганому прикладі, якщо ви не підтримуєте застарілий код, де цей стиль використовується і добре зрозумілий.


3
Ви можете переписати більшість мовних конструкцій, використовуючи if () та goto ..., що не виправдовує відмову від явної конструкції.
Shog9

2
Я знаю, але конструкції switch / case, які явно використовують "case 1: деякий випадок коду 2: some more case 3: final final break;" можна і потрібно переписати, використовуючи if / else if (моя думка).
steffenj

1
Зауважте, що перемикання може бути в багато разів швидше, ніж список if-elses. Switch використовує таблицю стрибків, або коли це неможливо, її умовні стрибки будуть структуровані у дереві рішень замість лінійного списку умовних стрибків. Добре структуровані вкладені заяви If-else будуть, у кращому випадку, однаково швидкими.
Йохім Куджперс

4

Це потужне і небезпечне. Найбільша проблема з просіданням полягає в тому, що це не явно. Наприклад, якщо ви стикаєтеся з часто редагуваним кодом, який має комутатор із пропусками, як ви знаєте, що це навмисно, а не помилка?

Де б я не користувався, я гарантую, що він коментується належним чином:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }

2
Намір очевидний, якщо для випадку "перший" немає коду. Якщо ви вводите код, а також хочете запустити код на випадок "другий", коментар важливий. Але я все одно уникаю цього сценарію.
Джоел Куехорн

1
@Joel - я повністю згоден. У моєму магазині у нас люди потрапляли, хоча коментарі в таких випадках, і я думаю, це зменшує читабельність. Якщо там є код, додайте в коментарі, що переходить.
Фред Ларсон

Що ми маємо на увазі "не явне". Ось чому ми ламаємо; Інші мови роблять це. Це лише питання для людей, які не вивчили свої мови по порядку. C # вимагає цього для випадку за замовчуванням ffs, без причини для імхо.
mckenzm

3

Використовувати пропуск, як у вашому першому прикладі, явно добре, і я б не вважав це справжнім провалом.

Другий приклад небезпечний і (якщо не коментується широко) неочевидний. Я вчу своїх студентів не користуватися такими конструкціями, якщо вони не вважають, що варто докладати зусиль, щоб виділити йому блок коментарів, який описує, що це навмисне проникнення, і чому це рішення краще, ніж альтернативи. Це перешкоджає неохайному використанню, але все ж робить це дозволеним у тих випадках, коли він звик до переваги.

Це більш-менш рівнозначно тому, що ми робили в космічних проектах, коли хтось хотів порушити стандарт кодування: вони повинні були подати заяву про видачу (і мене покликали проконсультувати щодо постанови).


2

Мені не подобається, що мої switchзаяви провалюються - це занадто схильний до помилок і важко читати. Єдиним винятком є ​​те, коли кілька caseзаяв роблять точно те саме.

Якщо є якийсь загальний код, який хочуть використовувати кілька гілок оператора перемикання, я витягую його в окрему загальну функцію, яку можна викликати в будь-якій гілці.


1

У деяких випадках використання пропусків - це лінь з боку програміста - вони могли використовувати ряд || наприклад, але натомість використовуйте ряд випадків перемикання «все-все».

Попри це, я вважаю, що вони особливо корисні, коли я знаю, що в підсумку мені знадобляться варіанти (наприклад, у відповіді меню), але ще не застосовано всіх варіантів. Крім того, якщо ви робите пропуск для обох "a" і "A", я вважаю, що набагато чистіше використовувати перехідний перемикач, ніж з'єднання if оператор.

Це, мабуть, питання стилю та того, як думають програмісти, але я, як правило, не люблю видаляти компоненти мови в ім'я "безпеки" - саме тому я схильний до C та його варіантів / нащадків більше, ніж, скажімо, Java. Мені подобається вміти маніпулювати навколо вказівниками тощо, навіть коли у мене немає «причин».


Строго кажучи. Оператори перемикання переходять через встановлене значення на купу певних місцеположень коду. Хоча компілятор, ймовірно, вловлює подібні речі, є значення швидкості та коректності для оператора перемикання для серії або операторів. Якщо p дорівнює 0 або p дорівнює 1 або p дорівнює 2. Я насправді повинен був оцінити, чи p було 0, а p було 1, а не перейти до місця p = 2 у коді. Що трапилося ідентично p0 та p1. Але мені ніколи не доводилося їх перевіряти.
Татариз

1

Прохідність повинна використовуватися лише тоді, коли вона використовується як таблиця стрибків у блок коду. Якщо є якась частина коду з безумовною перервою перед більшістю випадків, усі групи справ повинні закінчуватися таким чином.

Все інше - «зло».

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.