еще или возврат?

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

Code1

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

Или код 2

If(result)
{
   process1();
   return;
}
process2();
23
задан Ram 17 May 2010 в 11:46
поделиться

19 ответов

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

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

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

25
ответ дан 29 November 2019 в 00:39
поделиться

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

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

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

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

0
ответ дан 29 November 2019 в 00:39
поделиться

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

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

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

Например,

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

return returnValue;

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

0
ответ дан 29 November 2019 в 00:39
поделиться

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

0
ответ дан 29 November 2019 в 00:39
поделиться

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

1
ответ дан 29 November 2019 в 00:39
поделиться

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

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

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

Если результатом является какое-то условие «запретить выполнение», а «процесс1» - это просто очистка, необходимая при таком условии, вы должны выбрать Код 2. Это наиболее читаемый шаблон, если «процесс2» является основным действием функции.

if (NOT can_do_B)
{
  A() 
  return
}

B()
0
ответ дан 29 November 2019 в 00:39
поделиться

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

0
ответ дан 29 November 2019 в 00:39
поделиться

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

if(result)
{
  process 1
  return;
}

process 2
0
ответ дан 29 November 2019 в 00:39
поделиться

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

Хорошая практика - придерживаться одной точки выхода из рутины.

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

то есть:

if (date not set) return false;
age = calculateAgeBasedOnDate();
if (age higher than 100) return false;
...lots of code...
return result;
1
ответ дан 29 November 2019 в 00:39
поделиться

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

1
ответ дан 29 November 2019 в 00:39
поделиться

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

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

2
ответ дан 29 November 2019 в 00:39
поделиться

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

2
ответ дан 29 November 2019 в 00:39
поделиться

Они оба будут компилироваться в один и тот же IL для режима выпуска (в отладке может быть несколько разных операндов 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
3
ответ дан 29 November 2019 в 00:39
поделиться

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

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

править: Что касается производительности, испускаемый IL точно такой же. Выберите тот или иной стиль, ни за один из них нет штрафа.

3
ответ дан 29 November 2019 в 00:39
поделиться

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

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

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

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

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

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

DisplayHelloMessage(user.Name);

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

23
ответ дан 29 November 2019 в 00:39
поделиться

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

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

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

4
ответ дан 29 November 2019 в 00:39
поделиться

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

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

// do something if not result

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

32
ответ дан 29 November 2019 в 00:39
поделиться

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

 if (con){
    ...
    return;
 }
 if (other con){
    ...
    return;
 }
 ...
 return;
0
ответ дан 29 November 2019 в 00:39
поделиться

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

Обычно я делаю так:

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

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

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

1
ответ дан 29 November 2019 в 00:39
поделиться