web-dev-qa-db-ja.com

なぜ{...}やっと{...}が良いのでしょうか。 {...} catch {}が悪いですか?

特にキャッチが何もしない場合、引数なしでキャッチを使用するのは悪い形であると人々が言うのを見ました:

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
catch   // No args, so it will catch any exception
{}
reader.Close();

ただし、これは適切な形式と見なされます。

StreamReader reader=new  StreamReader("myfile.txt");
try
{
  int i = 5 / 0;
}
finally   // Will execute despite any exception
{
  reader.Close();
}

私が知る限り、finallyブロックにクリーンアップコードを置くこととtry..catchブロックの後にクリーンアップコードを置くことの唯一の違いは、tryブロックにreturnステートメントがある場合です(その場合、finallyのクリーンアップコードは実行されますが、try..catchの後のコードは実行されません)。

それ以外の場合、最終的に何が特別なのでしょうか?

193
mbeckish

大きな違いは、try...catchが例外を飲み込み、エラーが発生したという事実を隠すことです。 try..finallyはクリーンアップコードを実行し、その後、例外は処理を続行するため、処理方法を知っている何かによって処理されます。

349
Khoth

「最後に」は、「プログラムの状態が正常であることを確認するために常に実行する必要があること」のステートメントです。そのため、例外がプログラムの状態をスローオフする可能性がある場合は、1つにするのが常に適切な形式です。また、コンパイラは、Finallyコードが実行されることを保証するために非常に長くなります。

「キャッチ」は、「この例外から回復できます」というステートメントです。本当に修正できる例外からのみ回復する必要があります。引数なしのキャッチは、「ちょっと、何からでも回復できます!」と言いますが、これはほとんど常に真実ではありません。

Ifすべての例外から回復することが可能であった場合、それは本当にあなたが意図していることを宣言しているものについての意味論的なであろう。ただし、そうではなく、ほとんどの場合、特定の例外を処理するためのフレームの方が優れています。そのため、最後に使用して、クリーンアップコードを無料で実行しますが、さらに知識のあるハンドラーに問題を処理させます。

59
Adam Wright

その単一の行が例外をスローするとき、あなたはそれを知らないからです。

コードの最初のブロックでは、例外は単純にabsorbedになり、プログラムの状態が間違っている可能性がある場合でもプログラムは実行を続けます。

2番目のブロックでは、例外はスローになり、バブルアップしますが、reader.Close()は実行が保証されます。

例外が予期されない場合は、try..catchブロックをそのように配置しないでください。プログラムが悪い状態になったときに後でデバッグするのは難しく、その理由はわかりません。

32
chakrit

最後に何が実行されます。したがって、tryブロックが成功した場合は実行され、tryブロックが失敗した場合は、catchブロック、finallyブロックの順に実行されます。

また、次の構成要素を使用することをお勧めします。

using (StreamReader reader=new  StreamReader("myfile.txt"))
{
}

Usingステートメントはtry/finallyで自動的にラップされ、ストリームは自動的に閉じられます。 (実際に例外をキャッチする場合は、usingステートメントの周りにtry/catchを配置する必要があります)。

21
Mark Ingram

次の2つのコードブロックは同等ですが、同等ではありません。

try
{
  int i = 1/0; 
}
catch
{
  reader.Close();
  throw;
}

try
{
  int i = 1/0;
}
finally
{
  reader.Close();
}
  1. 「最後に」は意図を明らかにするコードです。このコードが何であれ実行する必要があることをコンパイラーや他のプログラマーに宣言します。
  2. 複数のcatchブロックがあり、クリーンアップコードがある場合は、最終的に必要です。最終的には、各catchブロックでクリーンアップコードを複製することになります。 (乾燥原理)

最後に、ブロックは特別です。 CLRは、catchブロックとは別に、finallyブロックを使用してコードを認識および処理し、CLRは、finallyブロックが常に実行されることを保証するために非常に長くなります。コンパイラーからの単なる構文糖ではありません。

8
Robert Paulson

私はここでコンセンサスと思われるものに同意します-空の「キャッチ」は、tryブロックで発生した可能性のある例外をマスクするため、悪いです。

また、読みやすさの観点から、「try」ブロックが表示された場合、対応する「catch」ステートメントがあると想定しています。リソースが「finally」ブロックで割り当て解除されるようにするために「try」のみを使用している場合は、代わりに 'using' statement を検討できます。

using (StreamReader reader = new StreamReader('myfile.txt'))
{
    // do stuff here
} // reader.dispose() is called automatically

IDisposableを実装する任意のオブジェクトで「using」ステートメントを使用できます。オブジェクトのdispose()メソッドは、ブロックの終わりに自動的に呼び出されます。

5
Chris Lawlor

Try..finallyブロックは、発生した例外を引き続きスローします。 finallyは、例外がスローされる前にクリーンアップコードが実行されることを確認します。

空のcatchを持つtry..catchは例外を完全に消費し、発生した事実を隠します。読者は閉じられますが、正しいことが起こったかどうかはわかりません。ファイルにiを書き込むことが目的だった場合はどうなりますか?この場合、コードのその部分には到達せず、myfile.txtは空になります。すべてのダウンストリームメソッドがこれを適切に処理しますか?空のファイルが表示されたら、例外がスローされたために空であると正しく推測できますか?例外をスローして、あなたが何か間違ったことをしていることを知らせておいたほうがよいでしょう。

もう1つの理由は、このように行われたtry..catchが完全に間違っていることです。これを行うことであなたが言っているのは、「何が起こっても、私はそれを処理できる」ということです。 StackOverflowExceptionはどうですか、その後クリーンアップできますか? OutOfMemoryExceptionはどうですか?一般に、予期する例外のみを処理し、処理方法を知っておく必要があります。

3
OwenP

メソッドが例外をローカルで処理する方法を知っている場合は、Try..Catch..Finallyを使用します。例外はTryで発生し、Catchで処理され、その後クリーンアップは最終的に行われます。

メソッドが例外の処理方法を知らないが、発生したクリーンアップが必要な場合は、Try..Finallyを使用します

これにより、例外は呼び出し元のメソッドに伝播され、呼び出し元のメソッドに適切なCatchステートメントがある場合に処理されます。現在のメソッドまたは呼び出し元のメソッドに例外ハンドラがない場合、アプリケーションはクラッシュします。

Try..Finallyにより、呼び出し元のメソッドに例外を伝播する前に、ローカルのクリーンアップが確実に行われます。

3
manjuv

どの例外タイプをキャッチするのか、またはどの例外タイプを使用するのかわからない場合、catchステートメントを作成しても意味がありません。どうすればよいかを知るために、状況についてのより多くの情報を持っている可能性のある上位の呼び出し元にそれを残す必要があります。

例外が発生した場合に備えて、finallyステートメントがまだあるはずです。そのため、その例外が呼び出し元にスローされる前にリソースをクリーンアップできます。

2
Mark Cidade

読みやすさの観点からは、将来のコードリーダーに「ここにあるものは重要です。何が起こっても実行する必要があります」と明示的に伝えています。これはいい。

また、空のcatchステートメントには、特定の「匂い」がある傾向があります。それらは、発生する可能性のあるさまざまな例外とそれらの処理方法を開発者が考えていないことを示している可能性があります。

2
Ryan

最後にオプションです。クリーンアップするリソースがない場合に「最終的に」ブロックする理由はありません。

2
Guy Starbuck

から取得: here

メソッドの正常な実行の一部として、例外の発生とキャッチが定期的に発生することはありません。クラスライブラリを開発する場合、クライアントコードには、例外が発生する可能性のある操作を実行する前に、エラー状態をテストする機会を与える必要があります。たとえば、System.IO.FileStreamは、Readメソッドを呼び出す前に確認できるCanReadプロパティを提供し、次のコードスニペットに示すように、潜在的な例外の発生を防ぎます。

Dim str As Stream = GetStream()If(str.CanRead)Then 'ストリームを読み取るためのコードEnd If

例外を発生させる可能性のある特定のメソッドを呼び出す前に、オブジェクトの状態をチェックするかどうかの決定は、オブジェクトの予想される状態に依存します。存在するファイルパスと読み取りモードでファイルを返すコンストラクタを使用してFileStreamオブジェクトを作成する場合、CanReadプロパティを確認する必要はありません。 FileStreamを読み取ることができないと、行われたメソッド呼び出しの予想される動作に違反し、例外が発生するはずです。対照的に、メソッドが読み取り可能または読み取り不可能なFileStream参照を返すように文書化されている場合、データの読み取りを試みる前にCanReadプロパティを確認することをお勧めします。

「例外まで実行」コーディング手法を使用することによるパフォーマンスへの影響を示すために、キャストが失敗した場合にInvalidCastExceptionをスローするキャストのパフォーマンスを、キャストが失敗した場合にnullを返すC#as演算子と比較します。 2つの手法のパフォーマンスは、キャストが有効な場合(テスト8.05を参照)で同じですが、キャストが無効であり、キャストを使用すると例外が発生する場合、キャストを使用すると、キャストを使用するよりも600倍遅くなります演算子として(テスト8.06を参照)。例外スロー手法のパフォーマンスへの影響には、例外の割り当て、スロー、およびキャッチのコストと、例外オブジェクトの後続のガベージコレクションのコストが含まれます。つまり、例外をスローする瞬間的な影響はそれほど高くありません。より多くの例外がスローされると、頻繁なガベージコレクションが問題になるため、例外スローコーディングテクニックを頻繁に使用することの全体的な影響は、テスト8.05と同様になります。

2

プログラマ向けC# を読むと、finallyブロックはアプリケーションを最適化し、メモリリークを防ぐように設計されていることを理解できます。

CLRはリークを完全に排除しません...プログラムが不注意に不要なオブジェクトへの参照を保持する場合、メモリリークが発生する可能性があります

たとえば、ファイルまたはデータベース接続を開くと、マシンはそのトランザクションに対応するためにメモリを割り当て、破棄または閉じるコマンドが実行されない限り、そのメモリは保持されません。ただし、トランザクション中にエラーが発生した場合、try.. finally..ブロック内にない限り、進行中のコマンドは終了しません。

catchfinallyとは異なり、catchはエラーを自分で処理/管理または解釈する方法を提供するように設計されているという意味です。 「ちょっと悪い奴らを捕まえたんだ、彼らに何をしてほしいの?」と言う人と考えてください。 finallyは、リソースが適切に配置されるように設計されています。誰かが悪い人がいるかどうかにかかわらず、彼があなたの財産がまだ安全であることを確認するだろうと考えてください。

そして、あなたはそれら2つが永久に一緒に働くことを許すべきです。

例えば:

try
{
  StreamReader reader=new  StreamReader("myfile.txt");
  //do other stuff
}
catch(Exception ex){
 // Create log, or show notification
 generic.Createlog("Error", ex.message);
}
finally   // Will execute despite any exception
{
  reader.Close();
}
2
dr.Crow

例外を再スローするためだけにcatch句を追加するのは悪い習慣です。

2

最後に、catchステートメントが呼び出し元プログラムに例外をスローした場合でも、リソースをクリーンアップできます。空のcatchステートメントを含む例では、ほとんど違いはありません。ただし、catchで何らかの処理を行ってエラーをスローする場合、または単にcatchをまったく持たない場合でも、finallyが実行されます。

1
Kibbee

1つは、気にしない例外をキャッチするのは悪い習慣です。 。Netパフォーマンスについての第5章 from。NETアプリケーションのパフォーマンスとスケーラビリティの改善を確認してください。サイドノート、おそらくtryブロック内にストリームをロードする必要があります。そうすれば、失敗した場合に適切な例外をキャッチできます。 tryブロックの外側にストリームを作成すると、その目的が無効になります。

1
Factor Mystic

try {…} catch {}は必ずしも悪いわけではありません。これは一般的なパターンではありませんが、スレッドの最後で(おそらく)開いているソケットを閉じるなど、リソースをシャットダウンする必要があるときに使用する傾向があります。

0
Martin Liesén

おそらく多くの理由の中でも、例外の実行は非常に遅いです。これが頻繁に発生する場合、実行時間を簡単に損なう可能性があります。

0
lotsoffreetime

すべての例外をキャッチするtry/catchブロックの問題は、未知の例外が発生した場合、プログラムが不確定な状態になることです。これは、フェイルファーストルールに完全に反します。例外が発生した場合、プログラムを続行しないようにします。上記のtry/catchはOutOfMemoryExceptionsもキャッチしますが、それは間違いなくプログラムが実行されない状態です。

Try/finallyブロックを使用すると、高速で失敗しながらクリーンアップコードを実行できます。ほとんどの状況では、すべての例外をグローバルレベルでのみキャッチするため、例外をログに記録してから終了できます。

0
David Mohundro

例外がスローされない限り、サンプル間の効果的な違いは無視できます。

ただし、「try」句の実行中に例外がスローされると、最初の例では例外が完全に飲み込まれます。 2番目の例では、コールスタックの次のステップに例外が発生するため、前述の例の違いは、1つは例外を完全に覆い隠し(最初の例)、もう1つ(2番目の例)は潜在的な後の処理のために例外情報を保持することです「finally」句のコンテンツを引き続き実行します。

たとえば、例外をスローした最初の例の 'catch'句にコードを配置した場合(最初に発生したもの、または新しいもの)、リーダークリーンアップコードは実行されません。最後に、「catch」句で発生することのとにかくを実行します。

したがって、「catch」と「finally」の主な違いは、「finally」ブロックの内容(まれな例外はいくつかあります)を考慮することができることです保証 「catch」句に続く(ただし「finally」句の外側にある)コードはそのような保証を行いませんが、予期しない例外です。

ちなみに、StreamとStreamReaderはどちらもIDisposableを実装しており、「using」ブロックにラップできます。 'using'ブロックはtry/finally( 'catch'なし)のセマンティックに相当するため、例は次のように簡潔に表現できます。

using (StreamReader reader = new  StreamReader("myfile.txt"))
{
  int i = 5 / 0;
}

... StreamReaderインスタンスがスコープ外になると、それを閉じて破棄します。お役に立てれば。

0
Jared