此代码是从ODBC连接的数据库读取和写入的应用程序的一部分。它在数据库中创建一条记录,然后检查是否已成功创建记录,然后返回true
。
我对控制流程的理解如下:
command.ExecuteNonQuery()
被记录为在“方法调用对该对象的当前状态无效”时抛出InvalidOperationException
。因此,如果发生这种情况,将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;
}
我在这里理解的错误是什么?
答案 0 :(得分:146)
Compiler Warning (level 2) CS0162
检测到无法访问的代码
编译器检测到永远不会执行的代码。
这只是说,编译器通过 Static Analysis 足够了解,无法达到它,并从已编译的IL中完全忽略了它(因此,您的警告)
注意 :您可以通过尝试使用调试器踩到“无法访问的代码”或使用IL来向自己证明这一事实资源管理器
finally
可能会在 Exception 上运行,(尽管除此之外)它并不会改变事实(在这种情况下)仍然是 Uncaught Exception < / em>。因此,最后一个return
永远不会受到打击。
如果您希望代码继续到最后一个return
,则唯一的选择是 Catch Exception ;
如果不这样做,请将其保留原样,然后删除return
。
示例
try
{
command.CommandText = sb.ToString();
returnValue = command.ExecuteNonQuery();
return returnValue == 1;
}
catch(<some exception>)
{
// do something
}
finally
{
command.Dispose();
}
return false;
引用文档
通过使用finally块,您可以清理所有 分配在try块中,即使出现异常也可以运行代码 发生在try块中。通常,finally块的语句 当控件离开try语句时运行。可以转移控制权 因正常执行,执行中断而发生 继续,转到或返回语句,或传播异常 在try语句之外。
在已处理的异常内,可以保证关联的finally块 要运行。 但是,如果未处理异常,则执行 finally块取决于异常展开操作的方式 触发。反过来,这取决于计算机的设置方式。
通常,当未处理的异常终止应用程序时,无论是 不是finally块运行并不重要。 但是,如果您有 在即使在这种情况下也必须运行的finally块中的语句, 一种解决方案是在try-finally语句中添加一个catch块。 或者,您可以捕获可能会在 在调用堆栈上方的try-finally语句的try块。那 是,您可以在调用该方法的方法中捕获异常 包含try-finally语句或在调用方法中 该方法,或调用堆栈中的任何方法。如果是 未捕获,是否执行finally块取决于是否 操作系统选择触发异常展开操作。
最后
使用支持IDisposable
接口(旨在释放非托管资源)的任何内容时,可以将其包装在using
语句中。编译器将生成try {} finally {}
并在对象上内部调用Dispose()
答案 1 :(得分:86)
将执行finally块,然后执行return false;在底部。
错了。 finally
不会吞下该异常。它很荣幸,并且异常将被正常抛出。它将仅在代码块结束之前(无论是否有异常)在代码的最后执行代码。
如果您希望吞下该异常,则应使用其中没有catch
的{{1}}块。
答案 2 :(得分:27)
警告是因为您没有使用catch
并且您的方法基本上是这样写的:
bool SomeMethod()
{
return true;
return false; // CS0162 Unreachable code detected
}
由于仅使用finally
进行处置,因此首选的解决方案是利用using
模式:
using(var command = new WhateverCommand())
{
...
}
这足以确保将调用Dispose
。保证可以在成功执行代码块之后或在调用堆栈中某些catch
down 之前(之前的父调用已关闭,对吗?)被调用。
如果不是要处理的话,那么
try { ...; return true; } // only one return
finally { ... }
就足够了,因为您将不需要,只需在方法末尾返回false
(不需要该行)。您的方法是命令执行的返回结果(true
或false
),或者将引发异常否则。
还考虑通过包装预期的异常来抛出自己的异常(签出InvalidOperationException constructor):
try { ... }
catch(SomeExpectedException e)
{
throw new SomeBetterExceptionWithExplanaition("...", e);
}
通常用于对调用者说一些比嵌套调用异常所讲的更有意义(有用)的东西。
大多数时候,您实际上并不关心未处理的异常。有时,即使未处理异常,也需要确保调用finally
。在这种情况下,您只需自己抓住并重新抛出(请参阅this answer):
try { ... }
catch { ...; throw; } // re-throw
finally { ... }
答案 3 :(得分:8)
您没有catch
块,因此仍会引发异常,从而阻止了返回。
将执行finally块,然后执行return false;在底部。
这是错误的,因为将执行finally块,然后会有未捕获的异常。
finally
块用于清除,它们不捕获异常。在返回之前抛出异常,因此,永远不会到达返回,因为在之前抛出了异常。
您的IDE是正确的,它将永远不会到达,因为将引发异常。只有catch
个块才能捕获异常。
从the documentation中读取,
通常,当未处理的异常结束应用程序时,是否运行finally块并不重要。但是,如果在即使在那种情况下都必须运行的finally块中有语句,则一种解决方案是在try-finally语句中添加catch块。或者,您可以捕获可能在调用堆栈上方的try-finally语句的try块中引发的异常。也就是说,您可以在调用包含try-finally语句的方法的方法中,在调用该方法的方法中或在调用堆栈中的任何方法中捕获异常。 如果未捕获到异常,则finally块的执行取决于操作系统是否选择触发异常展开操作。
这清楚地表明,finally并非旨在捕获异常,并且如果在catch
语句之前有空的finally
语句,那您将是正确的。
答案 4 :(得分:7)
引发异常时,堆栈将展开(执行将移出函数)而不返回值,并且函数上方堆栈帧中的任何catch块将捕获异常。
因此,<Label Content="{Binding Source=HelloWorld,
Converter={StaticResource ResourceConverter},
ConverterCulture=en-US}" />
将永远不会执行。
尝试手动引发异常以了解控制流:
return false
答案 5 :(得分:4)
最后一条语句return false
无法访问,因为try块缺少处理异常的catch
部分,因此在finally
块之后重新抛出了异常,并且执行从不到达最后一条语句。
答案 6 :(得分:2)
您的代码中有两个返回路径,由于第二个路径,第二个路径不可访问。 try
块return returnValue == 1;
中的最后一条语句提供了正常的返回值,因此您永远无法在方法块末尾到达return false;
。
FWIW,与finally
块相关的执行顺序是:将首先对在try块中提供返回值的表达式进行求值,然后将执行finally块,然后将计算出的表达式值设为返回(在try块内)。
关于异常的流程...如果没有catch
,则finally
将在异常发生之前执行,然后将异常从方法中抛出。没有“返回”路径。