Unreachable code, but reachable with an exception
此代码是应用程序的一部分,用于读取和写入连接到ODBC的数据库。它在数据库中创建一条记录,然后检查是否成功创建了一条记录,然后返回
我对控制流程的理解如下:
当"方法调用对于对象的当前状态无效"时,
但是,我的IDE声称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; } |
我理解这里的错误是什么?
编译器警告(2级)CS0162
Unreachable code detected
The compiler detected code that will never be executed.
也就是说,编译器通过静态分析已经足够了解了,以至于无法访问它,并且从编译的IL中完全忽略了它(因此会出现警告)。
注意:您可以通过尝试使用调试器或使用IL资源管理器单步执行无法访问的代码来向自己证明这一事实
如果您希望代码继续到最后一个
return ,您唯一的选择就是捕获异常;如果不这样做,只需保持原样,然后取下
return 。
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | try { command.CommandText = sb.ToString(); returnValue = command.ExecuteNonQuery(); return returnValue == 1; } catch(<some exception>) { // do something } finally { command.Dispose(); } return false; |
引用文档
最后尝试(C参考)
By using a finally block, you can clean up any resources that are
allocated in a try block, and you can run code even if an exception
occurs in the try block. Typically, the statements of a finally block
run when control leaves a try statement. The transfer of control can
occur as a result of normal execution, of execution of a break,
continue, goto, or return statement, or of propagation of an exception
out of the try statement.Within a handled exception, the associated finally block is guaranteed
to be run. However, if the exception is unhandled, execution of the
finally block is dependent on how the exception unwind operation is
triggered. That, in turn, is dependent on how your computer is set up.Usually, when an unhandled exception ends an application, whether or
not the finally block is run is not important. However, if you have
statements in a finally block that must be run even in that situation,
one solution is to add a catch block to the try-finally statement.
Alternatively, you can catch the exception that might be thrown in the
try block of a try-finally statement higher up the call stack. That
is, you can catch the exception in the method that calls the method
that contains the try-finally statement, or in the method that calls
that method, or in any method in the call stack. If the exception is
not caught, execution of the finally block depends on whether the
operating system chooses to trigger an exception unwind operation.
最后
当使用任何支持
the finally block would be executed, then would execute the return false; at the bottom.
错了。
如果你想吞下这个例外,你应该使用一个没有
警告是因为您没有使用
1 2 3 4 5 | bool SomeMethod() { return true; return false; // CS0162 Unreachable code detected } |
由于您只使用
1 2 3 4 |
这就足够了,以确保
如果不是处理,那么
1 2 | try { ...; return true; } // only one return finally { ... } |
这就足够了,因为您永远不必在方法的末尾返回
还可以考虑通过包装预期的异常来引发自己的异常(签出InvalidOperationException构造函数):
1 2 3 4 5 | try { ... } catch(SomeExpectedException e) { throw new SomeBetterExceptionWithExplanaition("...", e); } |
这通常用于对调用者说一些比嵌套调用异常更有意义(有用)的话。
大多数情况下,您并不真正关心未处理的异常。有时,即使未处理异常,也需要确保调用
1 2 3 | try { ... } catch { ...; throw; } // re-throw finally { ... } |
看起来,你在找这样的东西:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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; } } } |
请注意,
1 2 3 4 5 | finally { // This code will be executed; the exception will be efficently re-thrown } // And this code will never be reached |
您没有
the finally block would be executed, then would execute the return false; at the bottom.
这是错误的,因为将执行finally块,然后将出现未捕获的异常。
您的IDE是正确的,它永远不会被访问,因为异常将被抛出。只有
阅读文档,
Usually, when an unhandled exception ends an application, whether or not the finally block is run is not important. However, if you have statements in a finally block that must be run even in that situation, one solution is to add a catch block to the try-finally statement. Alternatively, you can catch the exception that might be thrown in the try block of a try-finally statement higher up the call stack. That is, you can catch the exception in the method that calls the method that contains the try-finally statement, or in the method that calls that method, or in any method in the call stack. If the exception is not caught, execution of the finally block depends on whether the operating system chooses to trigger an exception unwind operation.
这清楚地表明,finally并不打算捕获异常,如果在
当抛出异常时,堆栈将展开(执行将移出函数),而不返回值,并且函数上方堆栈帧中的任何catch块都将捕获异常。
因此,
尝试手动引发异常以了解控制流:
1 2 3 4 5 6 7 8 9 10 11 | try { command.CommandText = sb.ToString(); returnValue = command.ExecuteNonQuery(); // Try this. throw new Exception("See where this goes."); return returnValue == 1; } finally { command.Dispose(); } |
你的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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. } |
the finally block would be executed, then would execute the return false; at the bottom
这是您逻辑中的缺陷,因为
最后一条语句
代码中有两条返回路径,其中第二条由于第一条而无法访问。您的
fwiw,与
关于异常流…如果没有