c# exception unreachable-code

c# - Código inalcanzable, pero alcanzable con una excepción



exception unreachable-code (9)

Este código es parte de una aplicación que lee y escribe en una base de datos conectada ODBC. Crea un registro en la base de datos y luego verifica si un registro se ha creado con éxito, y luego devuelve true .

Mi comprensión del flujo de control es la siguiente:

Se documenta que command.ExecuteNonQuery() lanza una Invalid​Operation​Exception válida cuando "una llamada de método no es válida para el estado actual del objeto". Por lo tanto, si eso sucediera, la ejecución del bloque try se detendría, el bloque finally se ejecutaría, luego se ejecutaría el return false; en el fondo.

Sin embargo, mi IDE afirma que la return false; Es un código inalcanzable. Y parece ser cierto, puedo eliminarlo y compila sin ninguna queja. Sin embargo, para mí, parece que no habría ningún valor de retorno para la ruta del código donde se produce la excepción mencionada.

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; }

¿Cuál es mi error de comprensión aquí?


el bloque finally sería ejecutado, luego ejecutaría el retorno falso; en el fondo.

Incorrecto. finally no se traga la excepción. Lo honra y la excepción será lanzada normalmente. Solo ejecutará el código en el último antes de que finalice el bloque (con o sin una excepción).

Si desea que se trague la excepción, debe usar un bloque de catch sin throw en ella.


Cuando se lanza la excepción, la pila se desenrollará (la ejecución se moverá fuera de la función) sin devolver un valor, y cualquier bloque catch en los cuadros de pila encima de la función en su lugar detectará la excepción.

Por lo tanto, el return false nunca se ejecutará.

Intente lanzar manualmente una excepción para comprender el flujo de control:

try { command.CommandText = sb.ToString(); returnValue = command.ExecuteNonQuery(); // Try this. throw new Exception("See where this goes."); return returnValue == 1; } finally { command.Dispose(); }


En su código:

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. }

el bloque finally sería ejecutado, luego ejecutaría el retorno falso; en el fondo

Esta es la falla en su lógica porque el bloque finally no detectará la excepción y nunca alcanzará la última declaración de retorno.


La última declaración return false es inalcanzable, porque al bloque try le falta una parte catch que manejaría la excepción, por lo que la excepción se vuelve a generar después del bloque finally y la ejecución nunca llega a la última instrucción.


La advertencia es porque no usaste catch y tu método está escrito básicamente así:

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

Como finally se usa únicamente para desechar, la solución preferida es utilizar el patrón:

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

Eso es suficiente, para asegurar que se llame Dispose . Se garantiza que se llamará después de la ejecución exitosa del bloqueo de código o después (antes) de algún bloqueo en la pila de llamadas (las llamadas de los padres están inactivas, ¿verdad?).

Si no se trata de disponer, entonces

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

es suficiente, ya que nunca tendrá que devolver false al final del método (no hay necesidad de esa línea). Su método es el resultado de la ejecución del comando ( true o false ) o lanzará una excepción de lo contrario .

Considera también lanzar excepciones propias envolviendo las excepciones esperadas (revisa el constructor InvalidOperationException ):

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

Esto se suele usar para decir algo más significativo (útil) a la persona que llama de lo que sería una excepción de llamada anidada.

La mayoría de las veces no te importan las excepciones no controladas. A veces es necesario asegurarse de que finally se llame incluso si la excepción no se maneja. En este caso, simplemente lo atrapa usted mismo y lo vuelve a lanzar (vea esta respuesta ):

try { ... } catch { ...; throw; } // re-throw finally { ... }


No tiene un bloque catch , por lo que la excepción aún se lanza, lo que bloquea la devolución.

el bloque finally sería ejecutado, luego ejecutaría el retorno falso; en el fondo.

Esto es incorrecto, porque el bloque finalmente se ejecutaría, y luego habría una excepción no detectada.

finally bloques se utilizan para la limpieza y no detectan la excepción. La excepción se lanza antes de la devolución, por lo tanto, nunca se alcanzará la devolución, ya que se lanzó una excepción antes.

Su IDE es correcto y nunca se alcanzará, porque se lanzará la excepción. Solo catch bloques de catch pueden capturar excepciones.

Leyendo de docs.microsoft.com/en-us/dotnet/csharp/language-reference/… ,

Por lo general, cuando una excepción no controlada finaliza una aplicación, no importa si el último bloque se ejecute o no. Sin embargo, si tiene sentencias en un bloque finally que deben ejecutarse incluso en esa situación, una solución es agregar un bloque catch a la sentencia try-finally . Alternativamente, puede detectar la excepción que podría lanzarse en el bloque try de una instrucción try-finally más arriba en la pila de llamadas. Es decir, puede detectar la excepción en el método que llama al método que contiene la declaración try-finally, o en el método que llama a ese método, o en cualquier método en la pila de llamadas. Si la excepción no se detecta, la ejecución del bloque finally depende de si el sistema operativo elige o no desencadenar una excepción .

Esto muestra claramente que la última no tiene la intención de atrapar la excepción, y habría sido correcto si hubiera una declaración de catch vacía antes de la última.


Parece que estás buscando algo como esto:

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; } } }

Por favor, tenga en cuenta, que finally no se traga ninguna excepción.

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


Tiene dos rutas de retorno en su código, la segunda de las cuales es inalcanzable debido a la primera. La última declaración en su bloque try return returnValue == 1; proporciona su retorno normal, por lo que nunca puede alcanzar el return false; Al final del bloque de método.

FWIW, el orden de ejecución relacionado con el bloque finally es: primero se evaluará la expresión que proporciona el valor de retorno en el bloque try, luego se ejecutará el bloque finally y luego se devolverá el valor de expresión calculado (dentro del bloque try) .

Con respecto al flujo en la excepción ... sin catch , finally se ejecutará una excepción antes de que la excepción vuelva a ser eliminada del método; no hay camino de "retorno".


Advertencia del compilador (nivel 2) CS0162

Código inaccesible detectado

El compilador detectó un código que nunca será ejecutado.

Lo que está diciendo, el compilador entiende lo suficiente a través del análisis estático que no puede alcanzarse y lo omite por completo del IL compilado (de ahí su advertencia)

Nota : puede demostrar este hecho usted mismo al intentar Pasar al código inalcanzable con el depurador, o usar un IL Explorer

El finally puede ejecutarse en una Excepción , (aunque aparte de eso) no cambia el hecho (en este caso) seguirá siendo una Excepción no detectada . Ergo, la última return nunca será golpeada a pesar de todo.

  • Si desea que el código continúe en la última return , su única opción es capturar la excepción ;

  • Si no lo hace, simplemente déjelo como está y elimine la return .

Ejemplo

try { command.CommandText = sb.ToString(); returnValue = command.ExecuteNonQuery(); return returnValue == 1; } catch(<some exception>) { // do something } finally { command.Dispose(); } return false;

Citar la documentación.

docs.microsoft.com/en-us/dotnet/csharp/language-reference/…

Al usar un bloque finally, puede limpiar cualquier recurso que esté asignado en un bloque try, y puede ejecutar código incluso si ocurre una excepción en el bloque try. Normalmente, las declaraciones de un bloque finalmente se ejecutan cuando el control deja una declaración de prueba. La transferencia de control puede ocurrir como resultado de la ejecución normal, de la ejecución de una instrucción break, continue, goto o return, o de la propagación de una excepción fuera de la instrucción try.

Dentro de una excepción manejada, se garantiza que el bloque finalmente asociado se ejecute. Sin embargo, si la excepción no se maneja, la ejecución del bloque finally depende de cómo se desencadena la operación de desenrollamiento de la excepción. Eso, a su vez, depende de cómo esté configurada su computadora.

Por lo general, cuando una excepción no controlada finaliza una aplicación, no importa si el último bloque se ejecute o no. Sin embargo, si tiene sentencias en un bloque finally que deben ejecutarse incluso en esa situación, una solución es agregar un bloque catch a la sentencia try-finally . Alternativamente, puede detectar la excepción que podría lanzarse en el bloque try de una instrucción try-finally más arriba en la pila de llamadas . Es decir, puede detectar la excepción en el método que llama al método que contiene la declaración try-finally, o en el método que llama a ese método, o en cualquier método en la pila de llamadas. Si la excepción no se detecta, la ejecución del bloque finally depende de si el sistema operativo elige o no desencadenar una excepción.

Por último

Cuando use cualquier cosa que admita la interfaz IDisposable (que está diseñada para liberar recursos no administrados), puede envolverla en una declaración de using . El compilador generará un try {} finally {} y llamará internamente a Dispose() en el objeto