Иначе или вернуться?
Какой из следующих двух лучше всего подходит для производительности и стандартной практики. Как .NET внутренне обрабатывает эти два фрагмента кода?
Код1
If(result)
{
process1();
}
else
{
process2();
}
Или код 2
If(result)
{
process1();
return;
}
process2();
Ответы
Ответ 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 может быть ошибкой (в этом условии сделайте это ТОГДА ВСЕГДА это сделайте - я просто исправлю это для вас!). Раньше.
При компиляции они будут, как правило, одинаковыми, но нас не интересует производительность. Это касается читаемости и поддержки.