Почему оператор 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
}
Надеюсь, это поможет.