Должна ли функция иметь только один оператор возврата?

Matt Dillard написал :

return [[s autorelease] release];

blockquote>

Autorelease делает not сохранить объект. Autorelease просто помещает его в очередь для выпуска позже. Вы не хотите иметь там релиз.

781
задан 10 revs, 7 users 38% 21 February 2013 в 02:11
поделиться

45 ответов

Ну, возможно, я - один из нескольких человек, здесь достаточно взрослых для запоминания одной из больших причин, почему "только один оператор возврата" так боролся. Это - так компилятор, может испустить более эффективный код. Для каждого вызова функции компилятор обычно продвигает некоторые регистры на стеке сохранять свои значения. Таким образом, функция может использовать те регистры для временного хранения. Когда функция возвращается, те сохраненные регистры должны быть вытолканы от стека и назад в регистры. Это - один POP (или MOV - (SP), Rn) инструкция на регистр. Если у Вас есть набор операторов возврата, то любой, каждый должен вытолкать все регистры (который делает скомпилированный код больше) или компилятор должен отслеживать, которых регистры, возможно, были изменены и только выталкивают их (уменьшающий размер кода, но увеличивающий время компиляции).

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

2
ответ дан TMN 21 February 2013 в 13:11
поделиться
  • 1
    +1 ers, какая-либо обратная связь о том, как это работало на Вас? – TFD 26 February 2014 в 21:02

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

40
ответ дан 3 revs, 2 users 60% 21 February 2013 в 13:11
поделиться

Никто не упомянул или не процитировал Код завершения , поэтому я сделаю это.

17.1 return

Минимизировать количество возвратов в каждой подпрограмме . Труднее понять процедуру, если, читая ее внизу, вы не знаете о возможности ее возврата где-то выше.

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

355
ответ дан 22 November 2019 в 21:21
поделиться

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

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}
5
ответ дан 22 November 2019 в 21:22
поделиться

Есть ли веские причины, по которым лучше иметь только один оператор возврата в функции?

Да , есть:

  • Единственная точка выхода дает отличный место для утверждения ваших пост-условий.
  • Часто бывает полезна возможность поставить точку останова отладчика на один возврат в конце функции.
  • Меньшее количество возвратов означает меньшую сложность. Линейный код, как правило, проще для понимания.
  • Если попытка упростить функцию до одного возврата вызывает сложность, то это стимул к рефакторингу в сторону более мелких, более общих и более простых для понимания функций.
  • Если вы находитесь в язык без деструкторов или если вы не используете RAII, то один возврат уменьшает количество мест, которые вам нужно очистить.
  • Некоторые языки требуют единой точки выхода (например, Pascal и Eiffel).

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

Обновление : Очевидно рекомендации MISRA также продвигают единый выход .

Быть ясно, я не говорю, что всегда неправильно иметь несколько возвратов. Но, учитывая эквивалентные в остальном решения, есть много веских причин предпочесть вариант с одним возвратом.

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

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

38
ответ дан 22 November 2019 в 21:22
поделиться

Я заставляю себя использовать только один оператор return , поскольку он в некотором смысле генерирует запах кода. Позвольте мне объяснить:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(Условия являются произвольными ...)

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

  • Множественные возвраты
  • Рефакторинг в отдельные функции

Множественные возвраты

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

Отдельные функции

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

Конечно, это длиннее и немного беспорядочно, но в процессе рефакторинга функции таким образом мы

  • создали ряд функций многократного использования,
  • сделали функцию более удобочитаемой, а
  • функции сосредоточены на том, почему значения верны.
11
ответ дан 22 November 2019 в 21:22
поделиться

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

Не существует закона, требующего только одну точку выхода для метода на всех языках программирования . Некоторые люди настаивают на превосходстве этого стиля, а иногда возводят его в ранг «правила» или «закона», но это убеждение не подтверждается никакими доказательствами или исследованиями.

Использование более одного стиля возврата может быть плохой привычкой в ​​коде C, где ресурсы должны быть явно освобождены от распределения, но в таких языках, как Java, C #, Python или JavaScript, есть такие конструкции, как автоматическая сборка мусора и try..finally блокирует (и использует блоков в C #), и этот аргумент не применяется - в этих языках очень редко требуется централизованное ручное освобождение ресурсов.

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

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

Я говорил об этом более подробно в моем блоге .

10
ответ дан 22 November 2019 в 21:22
поделиться

Нет, потому что мы больше не живем в 1970-х. Если ваша функция настолько длинна, что множественные возвраты становятся проблемой, то она слишком длинная.

(Совершенно не считая того, что любая многострочная функция в языке с исключениями в любом случае будет иметь несколько точек выхода)

.
14
ответ дан 22 November 2019 в 21:22
поделиться

Вы знаете пословицу - красота в глазах смотрящего .

Некоторые люди используют NetBeans , некоторые - IntelliJ IDEA , некоторые - Python , а некоторые - PHP .

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

public void hello()
{
   if (....)
   {
      ....
   }
}

Все дело в видимости и удобстве обслуживания.

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

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

Я помню, 20 лет назад моего коллегу уволили за то, что сегодня назвали стратегией гибкой разработки . У него был тщательно продуманный план действий. Но его менеджер кричал на него: «Вы не можете постепенно предоставлять пользователям функции! Вы должны придерживаться водопада ». Он ответил менеджеру, что инкрементальная разработка будет более точной в соответствии с потребностями клиентов. Он верил в разработку для нужд клиентов, но менеджер верил в кодирование в соответствии с «требованиями клиента».

Мы часто виноваты в нарушении границ нормализации данных, MVP и MVC . Мы встраиваем вместо построения функции. Мы сокращаем путь.

Лично я считаю, что PHP - плохая практика, но что я знаю. Все теоретические аргументы сводятся к тому, чтобы попытаться выполнить один набор правил

качество = точность, ремонтопригодность. и рентабельность.

Все остальные правила отходят на второй план. И, конечно, это правило никогда не исчезает:

Лень - добродетель хорошего программист.

9
ответ дан 22 November 2019 в 21:22
поделиться

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

33
ответ дан 22 November 2019 в 21:22
поделиться

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

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

В примере:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

Я бы написал это так:

function() {
    HRESULT error = OPERATION1FAILED;//assume failure
    if(SUCCEEDED(Operation1())) {

        error = OPERATION2FAILED;//assume failure
        if(SUCCEEDED(Operation3())) {

            error = OPERATION3FAILED;//assume failure
            if(SUCCEEDED(Operation3())) {

                error = OPERATION4FAILED; //assume failure
                if(SUCCEEDED(Operation4())) {

                    error = S_OK;
                }
            }
        }
    }
    return error;
}

Что, безусловно, кажется лучше.

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

function() {
    HRESULT error = OPERATION1FAILED;//assume failure
    if(SUCCEEDED(Operation1())) {

        //allocate resource for op2;
        char* const p2 = new char[1024];
        error = OPERATION2FAILED;//assume failure
        if(SUCCEEDED(Operation2(p2))) {

            //allocate resource for op3;
            char* const p3 = new char[1024];
            error = OPERATION3FAILED;//assume failure
            if(SUCCEEDED(Operation3(p3))) {

                error = OPERATION4FAILED; //assume failure
                if(SUCCEEDED(Operation4(p2,p3))) {

                    error = S_OK;
                }
            }
            //free resource for op3;
            delete [] p3;
        }
        //free resource for op2;
        delete [] p2;
    }
    return error;
}

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

Но RAII делает вопрос о множественных выходных ресурсах спорным.

1
ответ дан 22 November 2019 в 21:22
поделиться

Наличие нескольких точек выхода, по сути, то же самое, что и использование GOTO . Плохо это или нет, зависит от того, как вы относитесь к хищникам.

3
ответ дан 22 November 2019 в 21:22
поделиться

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

Одно примечание. Если вы должны обработать возвращаемое значение перед возвратом в нескольких ситуациях, но не во всех, лучшие решения (IMHO) для определения такого метода, как ProcessVal, и его вызова перед возвратом:

var retVal = new RetVal();

if(!someCondition)
    return ProcessVal(retVal);

if(!anotherCondition)
   return retVal;
0
ответ дан 22 November 2019 в 21:22
поделиться

Если можно записать только свое мнение, то оно мое:

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

Эта привычка получать однократный возврат даже плохая для простого процедурного программирования, не говоря уже о более высокоуровневых абстракциях (функциональных, комбинаторных и т. Д.). И, кроме того, я хочу, чтобы весь код, написанный в этом стиле, прошел через специальный анализатор перезаписи, чтобы он имел несколько операторов возврата!

Функция (если это действительно функция / запрос в соответствии с примечанием «Разделение запросов и команд» - см., Например, язык программирования Eiffel) просто ДОЛЖНА определять столько точек возврата, сколько сценариев потока управления она имеет. Это намного яснее и математически последовательнее; и это способ написания функций (то есть запросов)

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

4
ответ дан 22 November 2019 в 21:22
поделиться

Меня, наверное, возненавидят за это, но в идеале я думаю, что вообще не должно быть no оператора return, функция должна просто возвращать свое последнее выражение, и должен в полностью идеальном случае содержать только один.

Так что не

function name(arg) {
    if (arg.failure?)
        return;

    //code for non failure
}

, а, скорее,

function name(arg) {
    if (arg.failure?)
        voidConstant
    else {
        //code for non failure


}

If-операторы, которые не являются выражениями и операторами return, для меня очень сомнительная практика.

0
ответ дан 22 November 2019 в 21:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: