Недоступный код, но достижимый за исключением

Этот код является частью приложения, которое выполняет чтение и запись в подключенную базу данных 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;
}

В чем здесь моя ошибка понимания?

Ответ 1

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

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

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

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

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

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

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

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

пример

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

    return returnValue == 1;
}
catch(<some exception>)
{
   // do something
}
finally 
{
    command.Dispose();
}

return false;

Цитировать документацию

try-finally (справочник по С#)

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

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

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

наконец

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

Ответ 2

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

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

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

Ответ 3

Предупреждение в том, что вы не использовали 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 { ... }

Ответ 4

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

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

Ответ 5

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

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

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

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

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

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

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

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

Ответ 6

Когда выбрасывается исключение, стек будет разматываться (выполнение будет выходить из функции) без возврата значения, и любой блок 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();
}

Ответ 7

В вашем коде:

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 не будет перехватывать исключение и никогда не достигнет последнего оператора return.

Ответ 8

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

Ответ 9

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

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

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