Почему оператор return должен предшествовать команде throw в блоке catch

Код ниже будет жаловаться

try
{
    session.Save(obj);
    return true;
}
catch (Exception e)
{
    throw e;
    return false;  // this will be flagged as unreachable code
}

тогда как это не будет:

try
{
    session.Save(obj);
    return true;
}
catch (Exception e)
{
    return false;
    throw e;
}

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

Ответ 1

return выйдет из метода; Метод throw также завершает работу метода, предполагая, что он не находится внутри try. Выход может быть только один раз!

Так что независимо от порядка - первый throw/return эффективно завершает метод.

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

try
{
    session.Save(obj);
    return true;
}
catch
{
    return false;
}

Лично я бы сказал, что это плохой код - он скрывает реальную проблему от вызывающей стороны, что делает его очень трудным для отладки. Это ничего не говорит нам о том, почему это не удалось. Я бы сказал, что лучший подход - просто позволить пузырю исключения. В этом случае нет смысла возвращать true, потому что мы никогда не вернем false - и нет смысла перехватывать исключение, просто чтобы перебросить его. Таким образом, весь метод становится:

session.Save(obj);

(больше ничего не требуется)


Если ваш вопрос "почему только один из них генерирует предупреждение": справедливый вопрос, но компилятор не обязан определять какой-либо из них для вас. Возможно, это должно заметить это. Я подозреваю, что gmcs обнаружит это и предупредит об этом - компилятор в моно гораздо охотнее указывает на глупость.


Редактировать: как и ожидалось, [g] mcs выводит:

Program.cs(15,13): предупреждение CS0162: обнаружен недоступный код

Program.cs(28,13): предупреждение CS0162: обнаружен недоступный код

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

class Program
{
    static void Main() { }
    static void DoSomething() { }
    bool ReturnFirst()
    {
        try
        {
            DoSomething();
            return true;
        }
        catch
        {
            return false;
            throw; // line 15
        }
    }
    bool ThrowFirst()
    {
        try
        {
            DoSomething();
            return true;
        }
        catch
        {
            throw;
            return false; // line 28
        }
    }
}

Ответ 2

Вы ошибаетесь: оба ваши примеры поднимают ошибку компилятора Dead code, потому что как throw, так и return отмечают точку выхода метода, и дальнейший код не допускается за пределами этой точки.

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

(ПРИМЕЧАНИЕ: этот вопрос был первоначально помечен как Java, а мое первое предложение относится к семантике компилятора Java)

Ответ 3

Потому что любой код после оператора return внутри блока кода будет недоступен.

Ответ 4

Этот ответ основан на С# и может быть или не быть применимым к Java.

В этом случае вам действительно не нужен оператор return. throw будет последним шагом функции.

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

ПРИМЕЧАНИЕ. Исключение из того, когда инструкция throw закончила бы эту функцию, заключается в том, что она должна быть обернута в блок try. В этом случае функция throw завершит выполнение оставшегося try блока кода и переместится в наиболее релевантный блок catch block - или finally, если не подходит catch.

Ваш код должен выглядеть так:

try
{
    session.Save(obj);

    return true;
}
catch(Exception e)
{
    throw e;
}

Тем не менее, нет смысла указывать попытку /catch в любом случае, если все, что вы делаете, повторно бросает исключение.


Чтобы конкретно ответить на ваш единственный вопрос:

Почему это нарушает мою теорию профессора?

Хорошо, либо ваш профессор ошибается, либо вы их неправильно поняли.

Ответ 5

"return false"; в блоке catch недоступен из-за "throw e;" как раз перед этим. Когда код выполняется в блоке catch, первая строка представляет собой бросок, что означает, что вы сразу же бросаете исключение на вызывающий метод, и поэтому любой следующий код не выполняется.

try
    {
        session.Save(obj);
        return true;
    }
    catch(Exception e)
    {
        throw e; //Throws exception to calling method
        return false;  //this will be flagged as unreachable code

    }

Надеюсь, это поможет.