web-dev-qa-db-ja.com

到達不能なコードですが、例外を使用して到達可能です

このコードは、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;
}

ここで私の理解の誤りは何ですか?

108
0xCAFEBABE

コンパイラ警告(レベル2)CS0162

到達不能コードが検出されました

コンパイラは、決して実行されないコードを検出しました。

ただ言っているのは、CompilerStatic Analysisを通じて十分に理解できるため、到達できず、コンパイルから完全に除外することです- [〜#〜] il [〜#〜] (したがって警告)

:この事実をあなた自身に証明することができますデバッガーを使用して、またはIL Explorerを使用して到達不能コードに進みます

finallyExceptionで実行される場合がありますが(それはさておき)、事実を変更しません(この場合)まだキャッチされなかった例外。エルゴ、最後のreturnは関係なくヒットすることはありません。

  • コードを最後のreturnまで続けたい場合、唯一のオプションはCatchtheException;

  • そうしない場合は、そのままにしてreturnを削除してください。

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

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

return false;
_

ドキュメントを引用するには

try-finally(C#リファレンス)

Finallyブロックを使用すると、tryブロックで割り当てられたリソースをクリーンアップでき、tryブロックで例外が発生した場合でもコードを実行できます。通常、finallyブロックのステートメントは、制御がtryステートメントから出るときに実行されます。制御の転送は、通常の実行、break、continue、goto、returnステートメントの実行、またはtryステートメントからの例外の伝播の結果として発生する可能性があります。

処理された例外内では、関連するfinallyブロックが実行されることが保証されています。 ただし、例外が処理されない場合、finallyブロックの実行は、例外のアンワインド操作がトリガーされる方法に依存します。これは、コンピューターのセットアップ方法に依存します。

通常、未処理の例外がアプリケーションを終了するとき、finallyブロックが実行されるかどうかは重要ではありません。 ただし、そのような状況でも実行する必要があるfinallyブロックにステートメントがある場合、1つの解決策はtry-finallyステートメントにcatchブロックを追加することです。または、コールスタックの上のtry-finallyステートメントのtryブロックでスローされる可能性のある例外をキャッチできます。つまり、try-finallyステートメントを含むメソッドを呼び出すメソッド、そのメソッドを呼び出すメソッド、または呼び出しスタック内のメソッドで例外をキャッチできます。例外がキャッチされない場合、finallyブロックの実行は、オペレーティングシステムが例外のアンワインド操作をトリガーすることを選択したかどうかによって異なります。

最後に

IDisposableインターフェイス(アンマネージリソースを解放するように設計されている)をサポートするものを使用する場合は、 using ステートメントでラップできます。コンパイラは_try {} finally {}_を生成し、オブジェクトに対してDispose()を内部的に呼び出します

149
Michael Randall

finallyブロックが実行され、次にreturn falseが実行されます。一番下に。

違う。 finallyは例外を飲み込みません。それを尊重し、例外は通常どおりスローされます。ブロックが終了する前に、例外の有無にかかわらず、最終的にのみコードを実行します。

例外を飲み込むには、catchを含まないthrowブロックを使用する必要があります。

86
Patrick Hofman

警告は、catchを使用しておらず、メソッドが基本的に次のように記述されているためです。

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

単にfinallyを破棄に使用するため、推奨される解決策はusingパターンを利用することです。

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

Disposeが呼び出されることを確認するには、これで十分です。コードブロックが正常に実行された後、またはコールスタック内のいくつかのcatchdownに(親コールがダウンしている、右に)呼び出されることが保証されています。

処分することではない場合は、

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

neverはメソッドの最後でfalseを返す必要があるため(この行は必要ありません)、十分です。メソッドは、コマンド実行の結果を返す(trueまたはfalse)か、そうでなければ例外をスローします。


予期される例外をラップすることで、独自の例外をスローすることも検討してください(チェックアウト InvalidOperationExceptionコンストラクター ):

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

これは通常、ネストされた呼び出し例外が伝えるよりも、呼び出し側にとって意味のある(有用な)ことを言うために使用されます。


ほとんどの場合、未処理の例外はあまり気にしません。例外が処理されない場合でも、finallyが呼び出されるようにする必要がある場合があります。この場合、あなたはそれを自分で捕まえて再投げるだけです( this answer を参照):

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

次のようなものを探しているようです。

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
13
Dmitry Bychenko

catchブロックがないため、例外が引き続きスローされ、戻りがブロックされます。

finallyブロックが実行され、次にreturn falseが実行されます。一番下に。

Finallyブロックが実行され、キャッチされない例外が発生するため、これは間違っています。

finallyブロックはクリーンアップに使用され、例外をキャッチしません。例外はスローされる前にスローされるため、例外がスローされる前にスローされるため、リターンに到達することはありません。

あなたのIDEは、例外がスローされるため、決して到達しないということは正しいです。catchブロックのみが例外をキャッチできます。

ドキュメント からの読み取り、

通常、未処理の例外がアプリケーションを終了するとき、finallyブロックが実行されるかどうかは重要ではありません。ただし、finallyブロックにステートメントがあり、その状況でも実行する必要がある場合、1つの解決策は、try-finallyステートメントにcatchブロックを追加することです。または、コールスタックの上位にあるtry-finallyステートメントのtryブロックでスローされる可能性のある例外をキャッチできます。つまり、try-finallyステートメントを含むメソッドを呼び出すメソッド、そのメソッドを呼び出すメソッド、または呼び出しスタック内のメソッドで例外をキャッチできます。 例外がキャッチされない場合、finallyブロックの実行は、オペレーティングシステムが例外のアンワインド操作のトリガーを選択するかどうかに依存します

これは、finallyが例外をキャッチすることを意図していないことを明確に示しており、catchステートメントの前に空のfinallyステートメントがあれば正しかったはずです。

8
Ray Wu

例外がスローされると、スタックは値を返すことなく巻き戻され(実行は関数から移動します)、関数の上のスタックフレーム内の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
Nisarg

あなたのコードで:

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ブロックが実行され、次にreturn falseが実行されます。下部に

finallyブロックは例外をキャッチせず、最後のreturnステートメントに到達しないため、これはロジックの欠陥です。

5
meJustAndrew

最後のステートメントreturn falseは到達不能です。これは、tryブロックに例外を処理するcatch部分がないため、finallyブロックの後に例外が再スローされ、実行が最後のステートメントに到達しないためです。

4
Martin Staufcik

コードには2つのリターンパスがあり、2番目のパスは最初のパスのために到達できません。 tryブロックの最後のステートメントreturn returnValue == 1;は通常のリターンを提供するため、return false;メソッドブロックの最後。

FWIW、finallyブロックに関連する実行順序は次のとおりです。tryブロックで戻り値を提供する式が最初に評価され、次にfinallyブロックが実行され、次に計算された式の値が返されます( tryブロック内)。

例外のフローについて... catchなしの場合、finallyは、例外がメソッドから再スローされる前に、例外時に実行されます。 「戻り」パスはありません。

2
C Robinson