Есть ли существенная разница между использованием if/else и switch-case в С#?

В чем преимущество/недостаток использования оператора switch по сравнению с if/else в С#. Я не могу себе представить, что есть большая разница, отличная, чем, может быть, внешний вид вашего кода.

Есть ли какая-либо причина, по которой результат IL или связанная с ней производительность выполнения будет радикально отличаться?

Связано: Что быстрее, включите строку или elseif по типу?

Ответ 1

Оператор SWITCH производит только ту же сборку, что и IFs, в режиме отладки или совместимости. В выпуске он будет скомпилирован в таблицу перехода (через оператор MSIL "switch" ) - это O (1).

С# (в отличие от многих других языков) также позволяет включать строковые константы - и это работает немного по-другому. Очевидно, нецелесообразно строить таблицы перехода для строк произвольной длины, поэтому чаще всего такой коммутатор будет скомпилирован в стек IF.

Но если количество условий достаточно велико для покрытия накладных расходов, компилятор С# создаст объект HashTable, заполнит его строковыми константами и выполнит поиск в этой таблице, а затем скачок. Поиск Hashtable не является строго O (1) и имеет заметные постоянные издержки, но если число ярлыков case велико, оно будет значительно быстрее, чем сравнение с каждой строковой константой в IFs.

Чтобы подвести итог, если число условий больше 5 или около того, предпочитайте ПЕРЕКЛЮЧАТЬ через IF, иначе используйте все, что выглядит лучше.

Ответ 2

В общем случае (учитывая все языки и все компиляторы) оператор switch CAN SOMETIMES более эффективен, чем оператор if/else, потому что компилятор легко генерировать таблицы перехода из операторов switch. Можно сделать то же самое для операторов if/else, учитывая соответствующие ограничения, но это намного сложнее.

В случае С# это также верно, но по другим причинам.

С большим количеством строк существует значительное преимущество в производительности для использования оператора switch, потому что компилятор будет использовать хеш-таблицу для реализации перехода.

С небольшим количеством строк производительность между ними одинакова.

Это потому, что в этом случае компилятор С# не создает таблицу перехода. Вместо этого он генерирует MSIL, который эквивалентен блокам IF/ELSE.

Существует инструкция "инструкция switch" MSIL, которая, когда jitted будет использовать таблицу перехода для реализации инструкции switch. Однако он работает только с целыми типами (этот вопрос задает строки).

Для небольших чисел строк более эффективно компилятор для генерации блоков IF/ELSE, тогда он должен использовать хеш-таблицу.

Когда я изначально заметил это, я сделал предположение, что, поскольку блоки IF/ELSE использовались с небольшим числом строк, компилятор сделал такое же преобразование для большого количества строк.

Это было НЕПРАВИЛЬНО. "IMA" был достаточно любезен, чтобы указать на это мне (ну... он не был добр к этому, но он был прав, и я был неправ, что является важной частью)

Я также сделал предположение о нехватке инструкции "switch" в MSIL (я понял, если был примитив коммутатора, почему они не использовали его с хеш-таблицей, поэтому не должно быть переключатель примитив....). Это было как неправильно, так и невероятно глупо с моей стороны. И снова "IMA" указал на это.

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

Однако, я сделал это Community Wiki, потому что я полагаю, что я не заслуживаю REP за неправильность. Если у вас есть шанс, пожалуйста, голосуйте за сообщение "ima".

Ответ 3

Три причины предпочесть switch:

  • Компилятор, ориентированный на собственный код, часто может компилировать оператор switch в одну условную ветвь плюс косвенный переход, тогда как для последовательности if требуется последовательность условных ветвей. В зависимости от плотности случаев было написано много научных работ о том, как эффективно составлять деловые заявления; некоторые из них связаны с страницей компилятора lcc. (У Lcc был один из самых инновационных компиляторов для коммутаторов.)

  • Оператор switch представляет собой выбор среди взаимоисключающих альтернатив, а синтаксис switch делает этот поток управления более прозрачным для программиста, затем гнездо if-then -секретные утверждения.

  • В некоторых языках, включая определенно ML и Haskell, , компилятор проверяет, не упустили ли вы какие-либо случаи. Я рассматриваю эту функцию как одно из главных преимуществ ML и Haskell. Я не знаю, может ли С# это сделать.

Анекдот: на лекции он дал получение награды за пожизненное достижение, я слышал, как Тони Хоар сказал, что из всего, что он делал в своей карьере, было три, которыми он больше всего гордился:

  • Изобретение Quicksort
  • Изобретая оператор switch (который Тони назвал оператором case)
  • Начало и завершение карьеры в отрасли

Я не могу представить себе жизнь без switch.

Ответ 4

Фактически, оператор switch более эффективен. Компилятор оптимизирует его в таблице поиска, где с операторами if/else это невозможно. Нижняя сторона заключается в том, что оператор switch не может использоваться с переменными значениями.
Вы не можете:

switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

он должен быть

switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}

Ответ 5

Компилятор собирается оптимизировать почти все в одном и том же коде с незначительными отличиями (Кнут, кто-нибудь?).

Отличие заключается в том, что оператор switch более чист, чем пятнадцать, если все остальные выражения связаны друг с другом.

Друзья не позволяют друзьям складывать инструкции if-else.

Ответ 6

Я не видел, чтобы кто-либо повышал (очевидную?) точку, что предполагаемое преимущество эффективности оператора switch зависит от того, насколько различные случаи примерно одинаково вероятны. В случаях, когда один (или несколько) значений намного более вероятен, лестница if-then-else может быть намного быстрее, гарантируя сначала проверку наиболее распространенных случаев:

Итак, например:

if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

против

switch(x) {
  case 0: 
         // do one thing
         break;
  case 1: 
         // do the other thing
         break;
  case 2: 
         // do the third thing
         break;
}

Если x равен нулю 90% времени, код "if-else" может быть в два раза быстрее, чем код на основе коммутатора. Даже если компилятор превращает "переключатель" в какой-то умный, управляемый таблицами goto, он все равно будет не так быстрым, как просто проверка нуля.

Ответ 7

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

Итак, если if/else выглядит лучше, используйте его, иначе используйте оператор switch.

Ответ 8

Боковая тема, но я часто беспокоюсь о (и чаще вижу). if/else и switch утверждение слишком велико для слишком большого числа случаев. Они часто повреждают ремонтопригодность.

Среди основных виновников:

  • Делать слишком много внутри нескольких операторов if
  • Другие случаи, которые можно анализировать по-человечески,
  • Слишком много условий в оценке if, чтобы знать, что искали.

Чтобы исправить:

  • Извлечение в метод рефакторинга.
  • Используйте словарь с указателями методов вместо случая или используйте IoC для дополнительной настройки. Также могут быть полезны фабрики методов.
  • Извлеките условия для собственного метода.

Ответ 9

Это на самом деле не отвечает на ваш вопрос, но учитывая, что между скомпилированными версиями будет небольшая разница, я настоятельно призываю вас написать свой код таким образом, который наилучшим образом описывает ваши намерения. Мало того, что компилятор делает больше, чем вы ожидаете, но это облегчит для других поддержку вашего кода.

Если вы намерены развернуть свою программу на основе значения одной переменной/атрибута, то оператор switch лучше всего представляет это намерение.

Если ваше намерение состоит в том, чтобы развернуть вашу программу на основе разных переменных/атрибутов/условий, тогда целая цепочка if/else if представляет это намерение.

Я дам, что коди прав, когда люди забывают команду break, но почти так же часто я вижу, что люди делают сложные, если блоки, где они получают {} неправильно, поэтому строки, которые должны быть в условном выражении, отсутствуют. Это одна из причин, по которой я всегда включаю {} в свои операторы if, даже если в ней есть одна строка. Это не только легче читать, но если мне нужно добавить еще одну строку в условное, я не могу забыть ее добавить.

Ответ 10

Если вы просто используете оператор if или else, базовое решение использует сравнение? Оператор

(value == value1) ? (type1)do this : (type1)or do this;

Вы можете выполнить или рутину в коммутаторе

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}

Ответ 11

Не просто С#, но все языки на основе C, я думаю: потому что переключатель ограничен константами, он может генерировать очень эффективный код с помощью "таблицы перехода". Случай C действительно хороший старый FORTRAN вычисляемый GOTO, но случай С# все еще испытывает против константы.

Это не тот случай, когда оптимизатор сможет сделать тот же код. Рассмотрим, например,

if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}

Поскольку это сложные булевы, сгенерированный код должен вычислить значение и короткую схему. Теперь рассмотрим эквивалентный

switch(a){
   case 3: // ...
    break;
   case 5:
   case 7: //...
    break;
   default: //...
}

Это можно скомпилировать в

BTABL: *
B3:   addr of 3 code
B5:
B7:   addr of 5,7 code
      load 0,1 ino reg X based on value
      jump indirect through BTABL+x

потому что вы неявно говорите компилятору, что ему не нужно вычислять тесты OR и равенства.

Ответ 12

Вопрос интереса. Это появилось несколько недель назад на работе, и мы нашли ответ, написав примерный фрагмент и просмотрев его в .NET Reflector (рефлектор потрясающий!! мне это нравится).

Это то, что мы обнаружили: Действительный оператор switch для чего-либо, кроме строки, скомпилируется в IL в качестве оператора switch. Однако, если это строка, она переписывается как if/else if/else в IL. Поэтому в нашем случае мы хотели знать, как операторы switch сравнивают строки, например, чувствительны к регистру и т.д., И рефлектор быстро дал нам ответ. Это было полезно знать.

Если вы хотите сравнить регистр по строкам с учетом регистра, то может использовать оператор switch, поскольку он быстрее, чем выполнение String.Compare в if/else. (Edit: Read Что быстрее, включите строку или elseif по типу? для некоторых фактических тестов производительности). Однако, если вы хотите сделать нечувствительность к регистру, тогда это лучше используя if/else, поскольку полученный код не очень хорош.

switch (myString.ToLower())
{
  // not a good solution
}

Лучшее эмпирическое правило заключается в использовании операторов switch, если это имеет смысл (серьезно), например:

  • улучшает читабельность вашего кода.
  • вы сравниваете диапазон значений (float, int) или enum

Если вам нужно манипулировать значением для подачи в оператор switch (создать временную переменную для переключения), вы, вероятно, должны использовать оператор управления if/else.

Обновление:

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


Небольшое примечание:

Чтобы улучшить читаемость операторов switch, попробуйте следующее:

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

Ответ 13

Оператор switch определенно быстрее, чем if if else if. Есть speedtest, которые были поставлены на него BlackWasp

http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

- Проверьте это

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

Ответ 14

Мой преподаватель cs предложил вам не переключать заявления, так как часто люди забывали перерыв или использовали его неправильно. Я не могу точно вспомнить, что он сказал, но что-то похожее на то, что касается некоторых базовых базовых кодов, в которых показывались примеры оператора switch (годы назад), также имело массу ошибок.

Ответ 15

Что-то, что я только что заметил, это то, что вы можете комбинировать if/else и switch statements! Очень полезно, когда нужно проверить предварительные условия.

if (string.IsNullOrEmpty(line))
{
    //skip empty lines
}
else switch (line.Substring(0,1))
{
    case "1":
        Console.WriteLine(line);
        break;
    case "9":
        Console.WriteLine(line);
        break;
    default:
        break;
}

Ответ 16

В соответствии с этой ссылкой IF vs Switch сравнение теста итерации с использованием оператора switch и if, похоже на 1,000,000,000 итераций, Время, затраченное на Switch Statement = 43.0s и If Statement= 48.0s

Это буквально 20833333 итераций в секунду. Итак, нужно ли нам больше сосредоточиться,

P.S: Просто знать разницу в производительности для небольшого списка условий.

Ответ 17

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

Напишите Программу для ввода любого числа (между 1 - 99) и проверьте, находится ли он в слоте a) 1 - 9, затем слот один b) 11 - 19, затем слот два c) 21-29, затем слот три и т.д. до 89-99

Затем, если вам нужно сделать много условий, кроме случая с переключением с сыном, вам нужно просто напечатать

Переключатель (нет/10)

и на случай 0 = 1-9, случай 1 = 11-19 и т.д.

будет так легко

Есть также много других подобных примеров!

Ответ 18

оператор switch обычно является сравнением для равенства. событие клавиатуры имеет большое преимущество перед оператором switch, когда он легко записывается и читает код, а если оператор elseif, отсутствующий {bracket}, может также беспокоить.

char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}

Если команда elseif отлично подходит для более чем одного решения, если (theAmountOfApples больше, чем 5 & theAmountOfApples менее 10), сохраните ваши яблоки else if (theAmountOfApples больше, чем 10 || theAmountOfApples == 100) продают ваши яблоки. Я не пишу С# или С++, но я изучил его, прежде чем я узнал Java, и они близки к языкам.

Ответ 19

Я знаю, что это не совсем вопрос, но мне действительно нужно указать, что когда вы думаете об эффективности на этом уровне, вам может понадобиться больше абстракции в вашем коде. Вам больше не понадобятся коммутационные шкафы, особенно если они содержат логику. (мой пример в php).

    $feeMapping = [
        1000 => 1,
        2000 => 2,
        3000 => 3,
        4000 => 4,
        5000 => 5,
        6000 => 6,
        7000 => 7
    ];

    function findFee($feeMapping, $amount) {
        foreach ($feeMapping as $fee => $value) {
            if ($value >= $amount) {
                return $fee;
            }
        }

        return 7;
    }

    $feeValue = findFee($feeMapping, 200);

Теперь посмотрим на избыточность аналогичного кода!

    if ($amount >= 1000) {
        return 1;
    } elseif ($amount >= 2000) {
        return 2;
    } elseif ($amount >= 3000) {
        return 3;
    } elseif ($amount >= 4000) {
        return 4;
    } elseif ($amount >= 5000) {
        return 5;
    } elseif ($amount >= 6000) {
        return 6;
    } else {
        return 7;
    }

Ответ 20

Одним из возможных недостатков операторов switch является отсутствие нескольких условий. У вас может быть несколько условий для операторов if (else), но не нескольких случаев с различными условиями в коммутаторе.

Операторы switch не подходят для логических операций, выходящих за рамки простых булевых уравнений/выражений. Для этих булевых уравнений/выражений он в высшей степени подходит, но не для других логических операций.

У вас гораздо больше свободы с логикой, доступной в операторах If, но читаемость может пострадать, если оператор If становится громоздким или плохо обрабатывается.

Оба имеют место в зависимости от контекста того, с чем вы столкнулись.