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

Иначе или вернуться?

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

Код1

If(result)
{
  process1();
}
else
{
  process2();
}

Или код 2

If(result)
{
   process1();
   return;
}
process2();
4b9b3361

Ответ 1

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

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

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

Ответ 2

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

if (result)
{
    // do something
    return;
}

// do something if not result

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

Ответ 3

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

Я бы сказал, что выбор между ними зависит от семантики.

'Если какое-то условие истинно, тогда сделайте это, в противном случае это "отлично отобразится на if-else.

if (isLoggedIn) {
    RedirectToContent();
} else {
    RedirectToLogin();
}

'Если какое-то условие затем выполняет некоторую очистку и выручает' лучше отображает код 2. Это называется шаблоном охраны. Это оставляет тело кода нормальным, ясным и незагрязненным ненужным отступом, насколько это возможно. Он обычно используется для проверки параметров или состояния (проверьте, что что-то пустое, или что-то кэшировано, что-то вроде этого).

if (user == null) {
    RedirectToLogin();
    return;
}

DisplayHelloMessage(user.Name);

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

Ответ 4

Если существует общий код, который должен быть выполнен после блока if/else, тогда параметр 1.

Если блок if-else - это последнее, что нужно сделать в функции, то опция 2.

Лично я всегда использую опцию 1. Если есть возвращаемое состояние, оно приходит после блока else

Ответ 5

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

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

edit: О производительности, испущенный ИЛ точно такой же. Выберите тот или иной стиль, нет штрафа.

Ответ 6

Они оба скомпилируются в один и тот же IL для режима Release (в Debug могут быть несколько разных Nop-операндов). И как таковые не будут иметь разницы в производительности. Это полностью зависит от того, как вы и ваша команда чувствовали, что код легче читать.

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

// C#
public static void @elseif(bool isTrue)
{
    if (isTrue)
        Process1();
    else
        Process2();
}
// IL
.method public hidebysig static void  elseif(bool isTrue) cil managed
{
  // Code size       15 (0xf)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  brfalse.s  IL_0009
  IL_0003:  call       void elseif.Program::Process1()
  IL_0008:  ret
  IL_0009:  call       void elseif.Program::Process2()
  IL_000e:  ret
} // end of method Program::elseif


// C#
public static void @earlyReturn(bool isTrue)
{
    if (isTrue)
    {
        Process1();
        return;
    }
    Process2();
}
// IL
.method public hidebysig static void  earlyReturn(bool isTrue) cil managed
{
  // Code size       15 (0xf)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  brfalse.s  IL_0009
  IL_0003:  call       void elseif.Program::Process1()
  IL_0008:  ret
  IL_0009:  call       void elseif.Program::Process2()
  IL_000e:  ret
} // end of method Program::earlyReturn

Ответ 7

Не могу сказать о производительности, но Code 1 выглядит намного яснее и логичнее для меня. Выпрыгивание из функции в середине блока if кажется довольно запутанным и легко упускать из вида.

Ответ 8

Здесь некоторое дополнительное чтение в предложении guard: http://www.c2.com/cgi/wiki?GuardClause

Один термин, который я не видел, упоминал, что я думаю, что важно - цель предложения охраны - повысить читаемость. Одиночные методы выхода могут иметь тенденцию к коду "стрелка" (где вложение инструкций делает стрелку).

Ответ 9

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

Ответ 10

Я бы использовал Code 1, потому что, если я добавлю что-то позже после инструкции if, я все же уверен, что он выполнит, без необходимости забывать удалить предложение return, где он находится в Code 2.

Ответ 11

Оба стиля являются обычным явлением, и над ними велись религиозные войны.: -)

Я обычно делаю это:

  • Если тест выражает семантику контракта метода, например. проверяя входные параметры для достоверности, затем выберите вариант 2.
  • В противном случае выберите вариант 1.

Однако, возможно, более важным правилом является "более читаемый и/или поддерживаемый для следующего разработчика, который смотрит на код?".

Разница в производительности незначительна, как говорили другие.

Ответ 12

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

Привязка к одной точке выхода для подпрограммы является хорошей практикой.

Однако иногда несколько возвратов делают код намного понятнее, особенно если у вас есть несколько тестов рядом с началом кода (т.е. проверка наличия всех входных параметров в правильном формате), для которых: if -true 'должен привести к возвращению.

т

if (date not set) return false;
age = calculateAgeBasedOnDate();
if (age higher than 100) return false;
...lots of code...
return result;

Ответ 13

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

if(result)
{
  process 1
  return;
}

process 2

Ответ 14

Я думаю, что второй вариант лучше для случаев многих условий (таких как проверка). Если вы используете первый в этих случаях, вы получите уродливое отступы.

 if (con){
    ...
    return;
 }
 if (other con){
    ...
    return;
 }
 ...
 return;

Ответ 15

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

Ответ 16

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

Ответ 17

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

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

Моя обычная практика состоит в том, чтобы иметь инструкцию if-else и один оператор return.

Например,

type returnValue;
if(true)
{
 returnValue = item;
}
else
 returnValue = somethingElse;

return returnValue;

По моему мнению, это немного читаемо. Однако это не всегда так. Иногда лучше иметь оператор return в середине оператора if, особенно если он требует сложного оператора возврата.

Ответ 18

Это зависит от того, что такое "результат", "процесс1" и "процесс2".

Если process2 является логическим следствием "результата", являющегося ложным; вы должны перейти на код 1. Это наиболее читаемый шаблон, если process1 и process2 являются эквивалентными альтернативами.

if (do_A_or_B = A)
  A()
else
  B()

Если результатом является какое-то условие "нет-go", а "process1" - это просто очистка, необходимая для такого условия, вы должны выбрать код 2. Это наиболее читаемый шаблон, если "process2" является основным действием функции.

if (NOT can_do_B)
{
  A() 
  return
}

B()

Ответ 19

Это зависит от контекста.

Если это функция, которая при вычислении значения возвращает это значение, как только оно у вас есть (вариант 2). Вы показываете, что сделали всю работу, в которой вы нуждаетесь, и уходите.

Если это код, который является частью логики программы, лучше всего быть максимально точным (вариант 1). Кто-то, смотрящий на вариант 1, будет знать, что вы имеете в виду (сделайте это ИЛИ это), где, поскольку вариант 2 может быть ошибкой (в этом условии сделайте это ТОГДА ВСЕГДА это сделайте - я просто исправлю это для вас!). Раньше.

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