无法访问的代码,但是有异常可以访问

0xC*_*ABE 108 c# exception unreachable-code

此代码是读取和写入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;
}
Run Code Online (Sandbox Code Playgroud)

我在这里理解的错误是什么?

AAA*_*ddd 149

编译器警告(等级2)CS0162

检测到无法访问的代码

编译器检测到永远不会执行的代码。

就是说,编译器通过“ 静态分析”了解到了无法达到的程度,并从编译的IL中完全忽略了它(因此请注意)

注意:您可以通过尝试使用调试器进入“无法访问的代码”或使用IL Explorer来证明这一事实。

finally可上运行异常,(尽管这一边),它不会改变的事实(在这种情况下),它仍然是一个未捕获的异常。嗯,最后一个return永远不会受到打击。

  • 如果您希望代码继续到最后return,则唯一的选择是捕获异常否则,请执行以下步骤。

  • 如果您不这样做,则将其保留原样,然后删除return

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

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

return false;
Run Code Online (Sandbox Code Playgroud)

引用文档

最后尝试(C#参考)

通过使用finally块,您可以清除try块中分配的所有资源,即使try块中发生异常,也可以运行代码。通常,当控制离开try语句时,finally块的语句运行。可以通过正常执行,执行break,continue,goto或return语句,或从try语句传播异常来进行控制转移。

在处理的异常内,可以保证关联的finally块可以运行。但是,如果未处理异常,则finally块的执行取决于如何触发异常展开函数。反过来,这取决于计算机的设置方式。

通常,当未处理的异常结束应用程序时,是否运行finally块并不重要。但是,如果在即使在那种情况下都必须运行的finally块中有语句,一种解决方案是在try-finally语句中添加catch块。或者,您可以捕获可能在调用堆栈上方的try-finally语句的try块中引发的异常。也就是说,您可以在调用包含try-finally语句的方法的方法中,在调用该方法的方法中或在调用堆栈中的任何方法中捕获异常。如果未捕获到异常,则finally块的执行取决于操作系统是否选择触发异常展开操作。

最后

使用支持IDisposable接口的任何东西(旨在释放非托管资源)时,可以将其包装在using语句中。编译器将在对象上生成try {} finally {}和内部调用Dispose()

  • @Clockwork * IL *是用高级.NET语言编写的代码的编译产品。编译用这些语言之一编写的代码后,您将获得由IL制成的二进制文件。请注意,中间语言有时也称为通用中间语言(CIL)或Microsoft中间语言(MSIL)。 (2认同)

Pat*_*man 86

将执行finally块,然后执行return false;在底部。

错误。finally不会吞下异常。它很荣幸,并且异常将被正常抛出。它将仅在代码块结束之前的finally中执行代码(有无异常)。

如果您希望吞下异常,则应使用catch其中没有no 的块throw

  • 它确实可以编译,但是它永远不会命中“ return false”,因为它将引发异常,而不是@EhsanSajjad (3认同)
  • 有趣的事实:如果程序中未捕获到异常,实际上**不能保证finally块将运行。规范并不能保证这一点,早期的CLR不能“执行” finally块。我认为从4.0开始(可能更早),行为发生了变化,但其他运行时的行为可能仍然有所不同。产生了令人惊讶的行为。 (3认同)
  • 编译器将只忽略该行,这是警告的意思。那为什么奇怪呢?@EhsanSajjad (2认同)

Sin*_*atr 27

警告是因为您没有使用,catch并且您的方法基本上是这样编写的:

bool SomeMethod()
{
    return true;
    return false; // CS0162 Unreachable code detected
}
Run Code Online (Sandbox Code Playgroud)

由于finally仅用于处置,因此首选的解决方案是利用using模式:

using(var command = new WhateverCommand())
{
     ...
}
Run Code Online (Sandbox Code Playgroud)

足以确保Dispose将要调用的内容。确保在成功执行代码块之后或在调用堆栈中的某些catch 中断(父调用已关闭,对吗?)之后(在此之前)被调用。

如果不是要处理的话

try { ...; return true; } // only one return
finally { ... }
Run Code Online (Sandbox Code Playgroud)

足够了,因为您将永远不必false在方法末尾返回(不需要该行)。你的方法是执行命令(任一返回结果truefalse)或将抛出一个异常,否则


还考虑通过包装预期的异常来抛出自己的异常(签出InvalidOperationException构造函数):

try { ... }
catch(SomeExpectedException e)
{
    throw new SomeBetterExceptionWithExplanaition("...", e);
}
Run Code Online (Sandbox Code Playgroud)

这通常用于对调用者说一些比嵌套调用异常所讲的更有意义(有用)的东西。


大多数时候,您实际上并不关心未处理的异常。有时,finally即使未处理异常,也需要确保调用了该方法。在这种情况下,您只需自己抓住它并重新抛出(请参阅此答案):

try { ... }
catch { ...; throw; } // re-throw
finally { ... }
Run Code Online (Sandbox Code Playgroud)


Dmi*_*nko 13

看来,您正在寻找这样的东西:

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这finally 不会吞噬任何异常

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

// And this code will never be reached
Run Code Online (Sandbox Code Playgroud)


Ray*_* Wu 8

您没有catch阻止,因此仍然引发异常,阻止返回。

将执行finally块,然后执行return false;在底部。

这是错误的,因为将执行finally块,然后会有未捕获的异常。

finally块用于清除,它们不捕获异常。在返回之前引发异常,因此,永远不会到达返回,因为在此之前引发了异常。

您的IDE是正确的,它将永远不会到达,因为将引发异常。只有catch块能够捕获异常。

阅读文档

通常,当未处理的异常结束应用程序时,是否运行finally块并不重要。但是,如果在即使在那种情况下都必须运行的finally块中有语句,则一种解决方案是在try-finally语句中添加catch块。或者,您可以捕获可能在调用堆栈上方的try-finally语句的try块中引发的异常。也就是说,您可以在调用包含try-finally语句的方法的方法中,在调用该方法的方法中或在调用堆栈中的任何方法中捕获异常。如果未捕获到异常,则finally块的执行取决于操作系统是否选择触发异常展开操作

这清楚地表明,finally并非旨在捕获异常,并且如果在该catch语句之前有一个空语句,那您将是正确的finally


Nis*_*arg 7

引发异常时,堆栈将展开(执行将移出函数)而不会返回值,并且函数上方的堆栈帧中的任何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();
}
Run Code Online (Sandbox Code Playgroud)


meJ*_*rew 5

在您的代码上:

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.
}
Run Code Online (Sandbox Code Playgroud)

将执行finally块,然后执行return false;在底部

这是您逻辑上的缺陷,因为该finally块不会捕获异常,并且永远不会到达最后一个return语句。