Подтвердить что ты не робот

С# else if() в сравнении с несколькими if()

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

Этот вопрос ( "использует" if/elseif/else "в сравнении с" if/else {if/else}"), но ему еще нужно ответить.

Как они практически различаются?

// approach one
if (x == 1)
    DoSomething();
else if (x == 2)
    DoSomethingElse();

// approach two
if (x == 1)
    DoSomething();
if (x == 2)
    DoSomethingElse();

Является ли полученный IL одинаковым?

4b9b3361

Ответ 1

Если DoSomething устанавливает x в 2, то они будут отличаться.

Ответ 2

[STAThread]
public static void Main()
{
    Int32 x = 1;

    if (x == 1)
        Console.WriteLine("1");
    else if (x == 2)
        Console.WriteLine("2");
}

Результаты в:

.method public hidebysig static void Main() cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .entrypoint
    .maxstack 2
    .locals init (
        [0] int32 x)
    L_0000: ldc.i4.1 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.1 
    L_0004: bne.un.s L_0011
    L_0006: ldstr "1"
    L_000b: call void [mscorlib]System.Console::WriteLine(string)
    L_0010: ret 
    L_0011: ldloc.0 
    L_0012: ldc.i4.2 
    L_0013: bne.un.s L_001f
    L_0015: ldstr "2"
    L_001a: call void [mscorlib]System.Console::WriteLine(string)
    L_001f: ret 
}

В то время как:

[STAThread]
public static void Main()
{
    Int32 x = 1;

    if (x == 1)
        Console.WriteLine("1");

    if (x == 2)
        Console.WriteLine("2");
}

Результаты в:

.method public hidebysig static void Main() cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .entrypoint
    .maxstack 2
    .locals init (
        [0] int32 x)
    L_0000: ldc.i4.1 
    L_0001: stloc.0 
    L_0002: ldloc.0 
    L_0003: ldc.i4.1 
    L_0004: bne.un.s L_0010
    L_0006: ldstr "1"
    L_000b: call void [mscorlib]System.Console::WriteLine(string)
    L_0010: ldloc.0 
    L_0011: ldc.i4.2 
    L_0012: bne.un.s L_001e
    L_0014: ldstr "2"
    L_0019: call void [mscorlib]System.Console::WriteLine(string)
    L_001e: ret 
}

IL-код немного отличается, и здесь основное различие:

Approach One: L_0004: bne.un.s L_0011 -> L_0011: ldloc.0 with L_0010: ret 
Approach Two: L_0004: bne.un.s L_0010 -> L_0010: ldloc.0 with no ret in between

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

Вот почему в первом подходе IL-код у вас есть директива "ret" сразу после вызова Console.WriteLine, а во втором - нет. В первом случае метод может быть завершен сразу после проверки, потому что не будет выполнено никаких проверок на x... во втором подходе вы должны следовать за ними последовательно, и поэтому ret только появляется на конец метода, никаких "ярлыков" до конца.

Для моего теста я использовал вызов Console.WriteLine()... но он уверен, что если DoSomething() связано с изменением значения переменной x, разница будет более важна в поведении кода. Скажем, что вместо локальной переменной x есть частный статический член (начальное значение всегда 1) и:

public void DoSomething()
{
    ++m_X;
}

В первом подходе, даже если m_X принимает значение 2 после того, как DoSomething() вызывается благодаря первой проверке, else сделает выход метода и DoSomethingElse() никогда не будет вызван. Во втором подходе будут называться оба метода.

Ответ 3

Обратите внимание, что в С# нет конструкции else if. Ваш первый пример кода точно такой же, как:

if (x == 1)
    DoSomething();
else 
{
    if (x == 2)
        DoSomethingElse();
}

Поскольку в else имеется только один оператор, фигурные скобки можно опустить, а для повышения удобочитаемости if обычно записывается в той же строке, что и предыдущая else. Запись нескольких инструкций "else if" эквивалентна дальнейшему вложению:

if (x == 1)
    DoSomething();
else 
{
    if (x == 2)
        DoSomethingElse();
    else
    {
        if (x == 3)
            YetSomethingElse();
        else
        {
            if (x == 4)
               ReallyDifferent();
        }
    }
}

Вышеприведенное может быть записано как:

if (x == 1)
    DoSomething();
else if (x == 2)
    DoSomethingElse();
else if (x == 3)
    YetSomethingElse();
else if (x == 4)
    ReallyDifferent();

Из этого вы можете видеть, что цепочка "else if" и if может давать разные результаты. В случае "else if" будет выполняться первая ветвь, которая удовлетворяет условию, и после этого дальнейшие проверки не выполняются. В случае цепочки if выполняются все ветки, удовлетворяющие их условиям.

Основное отличие здесь в том, что выполнение ветки приводит к тому, что последующее условие станет истинным. Например:

   var x = 1;

   if (x == 1)
       x = 2;
   else if (x == 2)
       x = 3;

VS

   var x = 1;

   if (x == 1)
       x = 2;

   if (x == 2)
       x = 3;

В первом случае x == 2, а во втором случае x == 3.

Ответ 4

Когда вы используете оператор else, будет выполняться только одна из ветвей (т.е. первая, которая соответствует условию if). Все остальные условия if даже не будут оценены:

// approach one
int x = 1;
if (x == 1)
    DoSomething(); //only this will be run, even if `DoSomething` changes `x` to 2
else if (x == 2)
    DoSomethingElse();

Пока вы не используете его, каждый из них может быть запущен (в зависимости от каждого из условий), т.е. каждый из них оценивается один за другим:

// approach two
int x = 1;
if (x == 1)
    DoSomething();//this is run, as `x` == 1
if (x == 2)
    DoSomethingElse();//if `DoSomething` changes `x` to 2, this is run as well

Итак, IL может отличаться.

Ответ 5

Когда вы вводите такой код

// approach two
if (x == 1)
    DoSomething();
if (x == 2)
    DoSomethingElse();

Каждый раз, когда проверяются условия.

Но когда вы вводите такой код

if (x == 1)
    DoSomething();
else if (x == 2)
    DoSomethingElse();

Если первое условие истинно, тогда он не проверяет следующее, если условие и, следовательно, уменьшает ненужную компиляцию.

Ответ 6

Нет комментариев о производительности?

Итак, если x = 1, вы делаете только одну проверку в первом случае, а во втором случае вы выполняете 2 проверки, поэтому первый случай выполняется быстрее.

Ответ 7

Если x изменяется (в Do Seomthing и DoSomethingElse), то первый оператор будет выполнять только один оператор. Во втором примере каждый оператор будет проверен (если, конечно, компилятор не оптимизирует его в таблице переходов для сравнения чисел).

Ответ 8

Когда вы используете несколько else if, он выполнит условие, которое соответствует. Если есть остальные случаи, они будут пропущены. Когда у вас есть несколько if, он будет проверять каждое утверждение. Таким образом, это становится проблемой производительности.

Ответ 9

Если x изменено несколькими потоками, возможно, что DoSomething() и DoSomethingElse() будут вызваны со вторым подходом

Ответ 10

Else, если будет оцениваться только в том случае, если первое условие не соответствует действительности. Но два последовательных оператора if будут оценены.

Вы можете легко протестировать эту концепцию в интерпретаторе Python:

Сначала запустите это:

Примечание. В Python elif используется вместо else, если

a = 1
if a >= 1:
  print('a is greater than or equal to 1')
elif a<=1:
  print('a is less than or equal to 1')

затем запустите этот

a=1
if a >= 1:
  print('a is greater than or equal to 1')
if a<=1:
  print('a is less than or equal to 1')