Код недоступен, но доступен за исключением - PullRequest
107 голосов
/ 12 марта 2019

Этот код является частью приложения, которое выполняет чтение и запись в базу данных, подключенную к ODBC. Он создает запись в базе данных и затем проверяет, была ли запись успешно создана, а затем возвращает true.

Мое понимание потока управления таково:

command.ExecuteNonQuery() задокументировано для выдачи Invalid​Operation​Exception, когда «вызов метода недопустим для текущего состояния объекта». Следовательно, если это произойдет, выполнение блока try остановится, будет выполнен блок finally, а затем будет выполняться return false; внизу.

Однако моя IDE утверждает, что return false; - это недоступный код. И это похоже на правду, я могу удалить его, и он собирается без каких-либо жалоб. Однако для меня это выглядит так, как если бы не было возвращаемого значения для пути к коду, где выбрасывается упомянутое исключение.

private static bool createRecord(String table,
                                 IDictionary<String,String> data,
                                 System.Data.IDbConnection conn,
                                 OdbcTransaction trans) {

    [... some other code ...]

    int returnValue = 0;
    try {
        command.CommandText = sb.ToString();
        returnValue = command.ExecuteNonQuery();

        return returnValue == 1;
    } finally {
        command.Dispose();
    }

    return false;
}

В чем моя ошибка понимания?

Ответы [ 9 ]

147 голосов
/ 12 марта 2019

Предупреждение компилятора (уровень 2) CS0162

Обнаружен недоступный код

Компилятор обнаружил код, который никогда не будет выполнен.

Что просто говорит, Компилятор достаточно понимает через Статический анализ , что он не может быть достигнут, и полностью исключает его из скомпилированного IL (отсюда и ваше предупреждение )

Примечание : Вы можете доказать это самому себе, попытавшись перейти к недоступному коду с помощью отладчика или используя IL Explorer

finally может работать на Exception , (хотя это в стороне), оно не меняет того факта (в данном случае), что все равно будет Uncaught Exception . Ergo, последний return никогда не получит удар в любом случае.

  • Если вы хотите, чтобы код продолжался до последнего return, ваш единственный вариант - Catch Exception ;

  • Если нет, просто оставьте все как есть и удалите return.

Пример * ** 1053 тысяча пятьдесят две * try { command.CommandText = sb.ToString(); returnValue = command.ExecuteNonQuery(); return returnValue == 1; } catch(<some exception>) { // do something } finally { command.Dispose(); } return false; Цитировать документацию

try-finally (C # Reference)

Используя блок finally, вы можете очистить любые ресурсы, которые размещены в блоке try, и вы можете запустить код, даже если исключение происходит в блоке try. Как правило, операторы блока finally запускается, когда управление покидает оператор try. Передача контроля может происходят в результате нормального выполнения, выполнения перерыва, оператор continue, goto, return или распространения исключения из оператора try.

В обработанном исключении связанный блок finally гарантирован быть запущенным. Однако, если исключение не обработано, выполнение Блок finally зависит от того, как выполняется операция по отмене исключения. срабатывает. Это, в свою очередь, зависит от того, как настроен ваш компьютер.

Обычно, когда необработанное исключение завершает приложение, или не окончательный блок выполняется не важно. Однако, если у вас есть операторы в блоке finally, который должен выполняться даже в такой ситуации, Одним из решений является добавление блока catch в оператор try-finally . В качестве альтернативы, вы можете поймать исключение, которое может быть брошено в блок try оператора try-finally выше стека вызовов . Тот есть, вы можете перехватить исключение в методе, который вызывает метод который содержит оператор try-finally или метод, который вызывает этот метод, или в любом методе в стеке вызовов. Если исключение не пойман, выполнение блока finally зависит от того, Операционная система выбирает запуск операции отмены исключения.

Наконец

При использовании всего, что поддерживает интерфейс IDisposable (предназначенный для освобождения неуправляемых ресурсов), вы можете заключить его в оператор using. Компилятор сгенерирует try {} finally {} и вызовет Dispose() для объекта

86 голосов
/ 12 марта 2019

блок finally будет выполнен, а затем выполнит return false; внизу.

Неправильно. finally не глотает исключение. Это чтит это, и исключение будет выброшено как обычно. Он будет выполнять код только в finally, до окончания блока (с исключением или без него).

Если вы хотите, чтобы исключение было проглочено, вы должны использовать блок catch без throw.

27 голосов
/ 12 марта 2019

Предупреждение в том, что вы не использовали catch и ваш метод в основном написан так:

bool SomeMethod()
{
    return true;
    return false; // CS0162 Unreachable code detected
}

Поскольку вы используете finally исключительно для утилизации, предпочтительным решением является использование шаблона using:

using(var command = new WhateverCommand())
{
     ...
}

Этого достаточно, чтобы гарантировать, что Dispose будет называться. Он гарантированно будет вызываться либо после успешного выполнения блока кода, либо после (до) некоторого catch вниз в стеке вызовов (родительские вызовы не работают, верно?).

Если речь не идет об утилизации, тогда

try { ...; return true; } // only one return
finally { ... }

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


Также рассмотрите возможность создания собственных исключений путем переноса ожидаемых исключений (см. Конструктор InvalidOperationException ):

try { ... }
catch(SomeExpectedException e)
{
    throw new SomeBetterExceptionWithExplanaition("...", e);
}

Это обычно используется для того, чтобы сказать вызывающему что-то более значимое (полезное), чем то, о котором говорит исключение вложенного вызова.


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

try { ... }
catch { ...; throw; } // re-throw
finally { ... }
13 голосов
/ 12 марта 2019

Кажется, вы ищете что-то вроде этого:

private static bool createRecord(string table,
                                 IDictionary<String,String> data,
                                 System.Data.IDbConnection conn,
                                 OdbcTransaction trans) {
  [... some other code ...]

  // Using: do not call Dispose() explicitly, but wrap IDisposable into using
  using (var command = ...) {
    try {
      // Normal flow:
      command.CommandText = sb.ToString();

      // True if and only if exactly one record affected
      return command.ExecuteNonQuery() == 1;
    }
    catch (DbException) {
      // Exceptional flow (all database exceptions)
      return false;
    }
  }
}

Обратите внимание, что finally не глотает любое исключение

finally {
  // This code will be executed; the exception will be efficently re-thrown
}

// And this code will never be reached
8 голосов
/ 13 марта 2019

У вас нет блока catch, поэтому выдается исключение, которое блокирует возврат.

будет выполнен блок finally, затем будет выполнен возврат false;внизу.

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

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

В вашей среде IDE правильно, что оно никогда не будет достигнуто, потому что будет выдано исключение.Только блоки catch способны отлавливать исключения.

Чтение из документации ,

Обычно, когда необработанное исключение завершает приложение, независимо от того, завершается ли оно или нетблок finally не важен.Однако, если у вас есть операторы в блоке finally, который должен выполняться даже в этой ситуации, одним из решений будет добавить блок catch в оператор try-finally .Кроме того, вы можете перехватить исключение, которое может быть выдано в блоке try оператора try-finally выше стека вызовов.То есть вы можете перехватить исключение в методе, который вызывает метод, содержащий оператор try-finally, или в методе, который вызывает этот метод, или в любом методе в стеке вызовов. Если исключение не перехвачено, выполнение блока finally зависит от того, решит ли операционная система запустить операцию отмены исключения .

Это ясно показывает, что finally не предназначеноперехватить исключение, и вы были бы правы, если бы до оператора finally был пустой оператор catch.

7 голосов
/ 12 марта 2019

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

Следовательно, return false никогда не выполнится.

Попробуйте вручную вызвать исключение, чтобы понять поток управления:

try {
    command.CommandText = sb.ToString();
    returnValue = command.ExecuteNonQuery();

    // Try this.
    throw new Exception("See where this goes.");

    return returnValue == 1;
} finally {
    command.Dispose();
}
5 голосов
/ 12 марта 2019

В вашем коде:

private static bool createRecord(String table, IDictionary<String,String> data, System.Data.IDbConnection conn, OdbcTransaction trans) {

    [... some other code ...]

    int returnValue = 0;
    try {
        command.CommandText = sb.ToString();
        returnValue = command.ExecuteNonQuery();

        return returnValue == 1; // You return here in case no exception is thrown
    } finally {
        command.Dispose(); //You don't have a catch so the exception is passed on if thrown
    }

    return false; // This is never executed because there was either one of the above two exit points of the method reached.
}

будет выполнен блок finally, затем будет выполнен возврат false;внизу

Это ошибка в вашей логике, поскольку блок finally не будет перехватывать исключение и никогда не достигнет последнего оператора возврата.

4 голосов
/ 12 марта 2019

Последний оператор return false недоступен, поскольку в блоке try отсутствует часть catch, которая обработает исключение, поэтому исключение перебрасывается после блока finally, и выполнение никогда не достигает последнего оператора.

2 голосов
/ 14 марта 2019

В вашем коде есть два пути возврата, второй из которых недоступен из-за первого.Последний оператор в вашем try блоке return returnValue == 1; обеспечивает ваш нормальный возврат, поэтому вы никогда не сможете достичь return false; в конце блока метода.

FWIW, порядок исключения, связанный с finally block is: выражение, предоставляющее возвращаемое значение в блоке try, будет сначала оценено, затем будет выполнен блок finally, а затем будет возвращено вычисленное значение выражения (внутри блока try).

Относительнопоток на исключение ... без catch, finally будет выполняться при исключении до того, как исключение будет затем выброшено из метода;нет «обратного» пути.

...