このコードは、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;
}
ここで私の理解の誤りは何ですか?
到達不能コードが検出されました
コンパイラは、決して実行されないコードを検出しました。
ただ言っているのは、CompilerはStatic Analysisを通じて十分に理解できるため、到達できず、コンパイルから完全に除外することです- [〜#〜] il [〜#〜] (したがって警告)
注:この事実をあなた自身に証明することができますデバッガーを使用して、またはIL Explorerを使用して到達不能コードに進みます
finally
はExceptionで実行される場合がありますが(それはさておき)、事実を変更しません(この場合)まだキャッチされなかった例外。エルゴ、最後の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;
_
ドキュメントを引用するには
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()
を内部的に呼び出します
finallyブロックが実行され、次にreturn falseが実行されます。一番下に。
違う。 finally
は例外を飲み込みません。それを尊重し、例外は通常どおりスローされます。ブロックが終了する前に、例外の有無にかかわらず、最終的にのみコードを実行します。
例外を飲み込むには、catch
を含まないthrow
ブロックを使用する必要があります。
警告は、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 { ... }
neverはメソッドの最後でfalse
を返す必要があるため(この行は必要ありません)、十分です。メソッドは、コマンド実行の結果を返す(true
またはfalse
)か、そうでなければ例外をスローします。
予期される例外をラップすることで、独自の例外をスローすることも検討してください(チェックアウト InvalidOperationExceptionコンストラクター ):
try { ... }
catch(SomeExpectedException e)
{
throw new SomeBetterExceptionWithExplanaition("...", e);
}
これは通常、ネストされた呼び出し例外が伝えるよりも、呼び出し側にとって意味のある(有用な)ことを言うために使用されます。
ほとんどの場合、未処理の例外はあまり気にしません。例外が処理されない場合でも、finally
が呼び出されるようにする必要がある場合があります。この場合、あなたはそれを自分で捕まえて再投げるだけです( this answer を参照):
try { ... }
catch { ...; throw; } // re-throw
finally { ... }
次のようなものを探しているようです。
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
catch
ブロックがないため、例外が引き続きスローされ、戻りがブロックされます。
finallyブロックが実行され、次にreturn falseが実行されます。一番下に。
Finallyブロックが実行され、キャッチされない例外が発生するため、これは間違っています。
finally
ブロックはクリーンアップに使用され、例外をキャッチしません。例外はスローされる前にスローされるため、例外がスローされる前にスローされるため、リターンに到達することはありません。
あなたのIDEは、例外がスローされるため、決して到達しないということは正しいです。catch
ブロックのみが例外をキャッチできます。
ドキュメント からの読み取り、
通常、未処理の例外がアプリケーションを終了するとき、finallyブロックが実行されるかどうかは重要ではありません。ただし、finallyブロックにステートメントがあり、その状況でも実行する必要がある場合、1つの解決策は、try-finallyステートメントにcatchブロックを追加することです。または、コールスタックの上位にあるtry-finallyステートメントのtryブロックでスローされる可能性のある例外をキャッチできます。つまり、try-finallyステートメントを含むメソッドを呼び出すメソッド、そのメソッドを呼び出すメソッド、または呼び出しスタック内のメソッドで例外をキャッチできます。 例外がキャッチされない場合、finallyブロックの実行は、オペレーティングシステムが例外のアンワインド操作のトリガーを選択するかどうかに依存します。
これは、finallyが例外をキャッチすることを意図していないことを明確に示しており、catch
ステートメントの前に空のfinally
ステートメントがあれば正しかったはずです。
例外がスローされると、スタックは値を返すことなく巻き戻され(実行は関数から移動します)、関数の上のスタックフレーム内の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();
}
あなたのコードで:
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ステートメントに到達しないため、これはロジックの欠陥です。
最後のステートメントreturn false
は到達不能です。これは、tryブロックに例外を処理するcatch
部分がないため、finally
ブロックの後に例外が再スローされ、実行が最後のステートメントに到達しないためです。
コードには2つのリターンパスがあり、2番目のパスは最初のパスのために到達できません。 try
ブロックの最後のステートメントreturn returnValue == 1;
は通常のリターンを提供するため、return false;
メソッドブロックの最後。
FWIW、finally
ブロックに関連する実行順序は次のとおりです。tryブロックで戻り値を提供する式が最初に評価され、次にfinallyブロックが実行され、次に計算された式の値が返されます( tryブロック内)。
例外のフローについて... catch
なしの場合、finally
は、例外がメソッドから再スローされる前に、例外時に実行されます。 「戻り」パスはありません。