Представьте себе следующий код:
void DoThis()
{
if (!isValid) return;
DoThat();
}
void DoThat() {
Console.WriteLine("DoThat()");
}
Можно ли использовать return внутри метода void? Есть ли снижение производительности? Или лучше написать такой код:
void DoThis()
{
if (isValid)
{
DoThat();
}
}
Ответы:
Возврат в методе void - это неплохо, это обычная практика инвертирования
if
операторов для уменьшения вложенности .А меньшая вложенность ваших методов улучшает читаемость кода и удобство сопровождения.
Фактически, если у вас есть метод void без какого-либо оператора return, компилятор всегда будет генерировать инструкцию ret в конце его.
источник
Есть еще одна веская причина для использования охранников (в отличие от вложенного кода): если другой программист добавляет код к вашей функции, он работает в более безопасной среде.
Рассмотреть возможность:
void MyFunc(object obj) { if (obj != null) { obj.DoSomething(); } }
против:
void MyFunc(object obj) { if (obj == null) return; obj.DoSomething(); }
Теперь представьте, что другой программист добавляет строку: obj.DoSomethingElse ();
void MyFunc(object obj) { if (obj != null) { obj.DoSomething(); } obj.DoSomethingElse(); } void MyFunc(object obj) { if (obj == null) return; obj.DoSomething(); obj.DoSomethingElse(); }
Очевидно, это упрощенный случай, но программист добавил сбой в программу в первом экземпляре (вложенный код). Во втором примере (ранний выход с охраной), как только вы преодолеете защиту, ваш код будет защищен от непреднамеренного использования нулевой ссылки.
Конечно, хороший программист не совершает таких ошибок (часто). Но профилактика лучше лечения - мы можем написать код таким образом, чтобы полностью исключить этот потенциальный источник ошибок. Вложенность добавляет сложности, поэтому передовой опыт рекомендует рефакторинг кода для уменьшения вложенности.
источник
Плохая практика ??? Ни за что. Фактически, всегда лучше обрабатывать проверки, возвращаясь из метода как можно раньше, если проверки не пройдут. В противном случае это привело бы к огромному количеству вложенных ifs & elses. Раннее завершение улучшает читаемость кода.
Также проверьте ответы на аналогичный вопрос: следует ли использовать оператор return / continue вместо if-else?
источник
Это неплохая практика (по всем уже указанным причинам). Однако чем больше у вас отдачи от метода, тем больше вероятность его разделения на более мелкие логические методы.
источник
В первом примере используется инструкция защиты. Из Википедии :
Я думаю, что наличие группы охранников на вершине метода - вполне понятный способ программирования. По сути, это означает, что «не выполняйте этот метод, если хотя бы один из них верен».
В общем, хотелось бы такого:
void DoThis() { if (guard1) return; if (guard2) return; ... if (guardN) return; DoThat(); }
Я думаю, тогда это будет намного удобнее:
void DoThis() { if (guard1 && guard2 && guard3) { DoThat(); } }
источник
Нет потери производительности, однако второй фрагмент кода более читабелен и, следовательно, его легче поддерживать.
источник
В этом случае ваш второй пример - лучший код, но он не имеет ничего общего с возвратом из функции void, это просто потому, что второй код более прямой. Но возврат из функции void - это нормально.
источник
Это нормально и без «потери производительности», но никогда не пишите оператор «if» без скобок.
Всегда
if( foo ){ return; }
Это более читабельно; и вы никогда случайно не предположите, что некоторые части кода находятся внутри этого оператора, хотя это не так.
источник
{
. Это совпадает{
с вашим}
в одном столбце, что значительно облегчает читаемость (гораздо проще найти соответствующие открывающие / закрывающие фигурные скобки).if
операторы требуют закрывающих фигурных скобок, будет легко, и, таким образом,if
управление операторами одним оператором будет безопасным. Перемещение открытой фигурной скобки обратно к строке с помощьюif
сохраняет строку вертикального пространства в каждом мульти-оператореif
, но потребует использования строки закрывающей скобки, которая в противном случае была бы ненужной.Я собираюсь не согласиться со всеми вами, молодые хищники, по этому поводу.
Использование return в середине метода, недействительного или нет, является очень плохой практикой по причинам, которые были довольно четко сформулированы почти сорок лет назад покойным Эдсгером В. Дейкстра, начиная с хорошо известного «Заявление GOTO, которое считается вредным. ", и продолжается в" Структурированном программировании "Даля, Дейкстры и Хора.
Основное правило состоит в том, что каждая структура управления и каждый модуль должны иметь ровно одну запись и один выход. Явный возврат в середине модуля нарушает это правило и значительно затрудняет рассуждение о состоянии программы, что, в свою очередь, значительно затрудняет определение правильности программы или нет (что является гораздо более сильным свойством. чем «работает он или нет»).
«Заявление GOTO считается вредным» и «Структурированное программирование» положили начало революции «структурированного программирования» 1970-х годов. Эти две части являются причиной того, что у нас сегодня есть if-then-else, while-do и другие явные управляющие конструкции, и почему операторы GOTO в языках высокого уровня находятся в списке исчезающих видов. (Мое личное мнение, что они должны быть в списке вымерших видов.)
Стоит отметить, что Модулятор потока сообщений, первая часть военного программного обеспечения, которая КОГДА-ЛИБО прошла приемочные испытания с первой попытки, без отклонений, отказов или словоблудия «да, но», была написана на языке, который даже не имел заявление GOTO.
Также стоит упомянуть, что Никлаус Вирт изменил семантику оператора RETURN в Oberon-07, последней версии языка программирования Oberon, сделав его конечной частью объявления типизированной процедуры (т. Е. Функции), а не исполняемый оператор в теле функции. В его объяснении изменения говорилось, что он сделал это именно потому, что предыдущая форма БЫЛА нарушением принципа одного выхода структурного программирования.
источник
При использовании охранников обязательно следуйте определенным правилам, чтобы не запутать читателей.
пример
// guards point you to the core intent void Remove(RayCastResult rayHit){ if(rayHit== RayCastResult.Empty) return ; rayHit.Collider.Parent.Remove(); } // no guards needed: function split into multiple cases int WonOrLostMoney(int flaw)=> flaw==0 ? 100 : flaw<10 ? 30 : flaw<20 ? 0 : -20 ;
источник
Выбрасывать исключение вместо того, чтобы ничего не возвращать, когда объект равен нулю и т. Д.
Ваш метод ожидает, что объект не будет нулевым, и это не так, поэтому вы должны выбросить исключение и позволить вызывающему обработать это.
Но в остальном досрочный возврат - неплохая практика.
источник