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

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

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

по теме: что быстрее, включить строку или elseif по типу?

20 ответов


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

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

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

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


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

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

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

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

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

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

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

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

Это было неправильно. "ИМА" была достаточно любезна, чтобы указать мне на это (ну...он не был добрым, но он был прав, а я ошибался, что является важной частью)

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

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

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


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

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

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

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

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

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

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


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

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

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

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

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

разница в том, что оператор switch чище, чем пятнадцать, если еще заявления нанизанные.

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


Я не видел, чтобы кто-то еще поднимал (очевидно?) укажите, что предполагаемое преимущество эффективности оператора 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
}

vs

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 может быть в два раза быстрее, чем switch-based код. Даже если компилятор превратит "коммутатор" в какой-то умный настольный goto, он все равно не будет таким быстрым, как просто проверка на ноль.


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

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


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

общие виновники включают в себя:

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

В исправить:

  1. извлечь в рефакторинге.
  2. используйте словарь с указателями методов вместо регистра или используйте IoC для дополнительной конфигурируемости. Фабрики метода также могут быть полезны.
  3. извлеките условия своим собственным методом

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

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

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

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

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

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

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

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


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

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

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

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


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

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

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 и equality.


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

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

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

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

лучшее эмпирическое правило-использовать операторы switch, если это имеет смысл (серьезно), e.g:

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

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

обновление:

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


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

для улучшения читаемости инструкций попробовать следующий:

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

согласно этой ссылке,IF vs Switch сравнение итерационного теста с использованием switch и if statement, похоже на 1,000,000,000 итераций, время, затраченное переключатель утверждение= 43.0 s & by Если Заявление = 48.0 s

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

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


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


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

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

Я Думаю, Что Переключатель Быстрее, Чем Если Условия как узнать, есть ли такая программа, как :

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

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

переключатель (no / 10)

и в случае 0 = 1-9, случай 1 = 11-19 и так далее

Это будет так просто

Есть Еще Много Таких Примеров!


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

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

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


Я знаю, что это не совсем вопрос, но мне действительно нужно указать, что когда вы думаете об эффективности на этом уровне, вам может понадобиться больше абстракции в вашем коде. Вам больше не понадобятся переключатели, особенно если они содержат логику. (мой пример в 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;
    }

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

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

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

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