エラー処理(例外処理)を行うエラー処理(例外処理)とは、エラーが発生した時にそれを検出し、適切な処理を行うことです。ここでは具体例を示して、エラー処理の基本を説明します。 ここでは、次のようなメソッドについて考えます。このメソッドは、ファイルを開いて、その内容をStringオブジェクトとして返すものです。なお、このコードについて詳しくは、こちらをご覧ください。 [VB.NET] Public Function ReadAllText(ByVal filePath As String) As String Dim sr As System.IO.StreamReader 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) '内容をすべて読み込む Dim s As String = sr.ReadToEnd() '閉じる sr.Close() '結果を返す Return s End Function [C#] public string ReadAllText(string filePath) { System.IO.StreamReader sr; //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); //内容をすべて読み込む string s = sr.ReadToEnd(); //閉じる sr.Close(); //結果を返す return s; } Try...Catchですべての種類の例外をキャッチする上記のコードでは、エラーが発生する可能性が十分あります。例えば、File.OpenTextメソッドに存在しないファイルを指定すると、エラーが発生しますし、その他の理由によってもエラーが発生する可能性があります。File.OpenTextメソッドを呼び出した時にエラーが発生しても、エラーを出さずに、String.Empty("")を返すようにするには、次のようにします。 [VB.NET] Public Function ReadAllText(ByVal filePath As String) As String Dim sr As System.IO.StreamReader Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As Exception '例外をキャッチした時 '例外を説明するメッセージを表示 Console.WriteLine(ex.Message) '空の文字列を返す Return String.Empty End Try '内容をすべて読み込む Dim s As String = sr.ReadToEnd() '閉じる sr.Close() '結果を返す Return s End Function [C#] public string ReadAllText(string filePath) { System.IO.StreamReader sr; try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (Exception ex) { //例外をキャッチした時 //例外を説明するメッセージを表示 Console.WriteLine(ex.Message); //空の文字列を返す return string.Empty; } //内容をすべて読み込む string s = sr.ReadToEnd(); //閉じる sr.Close(); //結果を返す return s; } File.OpenTextメソッドを呼び出した時にエラーが発生すると、例外が発生します(スローされます)。上記のようにTry...Catchを使うことにより、この例外をキャッチ(捕捉)することができます。 上の例の場合、TryブロックにOpenTextメソッドを入れることにより、OpenTextメソッドで発生する例外をキャッチするようにしています。Tryブロック内で例外が発生すると、Catchブロックに制御がジャンプし、実行されます。この時、例外の情報をExceptionオブジェクトとして取得することができます。 Exceptionオブジェクトには、問題の解決に役立つ様々な情報が入っています。Messageプロパティには例外に関する説明が、Sourceプロパティにはエラーの原因となったオブジェクトやアセンブリの名前が、StackTraceプロパティにはスタックトレースの情報が入っています。さらに詳しくは、MSDN等でExceptionクラスをお調べください。 上記の例では、例外をキャッチすると、例外の説明を表示して、空の文字列を返しています。 できるだけエラーが発生しないようにする例外が発生すると、多くのリソースを消費します。例外は、できるだけ発生しないようにすべきです。 例えば上記の例で、OpenTextメソッドを呼び出す前にファイルが存在しているかを調べることは、ごく簡単です。事前に調べることにより、例外が発生する機会をかなり減らすことができます。 [VB.NET] Public Function ReadAllText(ByVal filePath As String) As String 'ファイルが存在しているか調べる If Not System.IO.File.Exists(filePath) Then Return String.Empty End If Dim sr As System.IO.StreamReader Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As Exception '例外をキャッチした時 '例外を説明するメッセージを表示 Console.WriteLine(ex.Message) '空の文字列を返す Return String.Empty End Try '内容をすべて読み込む Dim s As String = sr.ReadToEnd() '閉じる sr.Close() '結果を返す Return s End Function [C#] public string ReadAllText(string filePath) { //ファイルが存在しているか調べる if (!System.IO.File.Exists(filePath)) { return string.Empty; } System.IO.StreamReader sr; try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (Exception ex) { //例外をキャッチした時 //例外を説明するメッセージを表示 Console.WriteLine(ex.Message); //空の文字列を返す return string.Empty; } //内容をすべて読み込む string s = sr.ReadToEnd(); //閉じる sr.Close(); //結果を返す return s; } ただしこのようにしても、ファイルが存在しないことによるエラーがOpenTextメソッドで発生する可能性があることに注意してください。File.Existsメソッドでファイルのチェックを行った後、OpenTextメソッドが呼び出される前にそのファイルが削除される可能性があります。 特定の種類の例外をキャッチする例外には様々な種類があり、エラーが発生した理由により、スローされる例外も異なります。 例えばFile.OpenTextメソッドでは、指定されたファイルが存在しなかった場合は、FileNotFoundExceptionという例外がスローされます。OpenTextメソッドにNULLが指定された場合はArgumentNullExceptionが、必要なアクセス許可が無かった場合はUnauthorizedAccessExceptionがスローされます。どのような例外がスローされるかは、ヘルプやMSDNを調べることにより、ある程度分かります(全て書いてある訳ではありません)。実際にMSDNでFile.OpenText メソッドをご覧になって、どのような例外があるかご確認ください。 以下に、ある特定の種類の例外だけをキャッチする例を示します。FileNotFoundExceptionだけをキャッチするには、次のようにします。 [VB.NET] Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As System.IO.FileNotFoundException 'FileNotFoundExceptionをキャッチした時 Console.WriteLine(ex.Message) Return String.Empty End Try [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (System.IO.FileNotFoundException ex) { //FileNotFoundExceptionをキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } CatchでExceptionを指定するとすべての例外をキャッチしますが、このようにキャッチする例外の種類を指定することもできます。 Catchブロックを追加するFileNotFoundExceptionだけでなく、UnauthorizedAccessExceptionがスローされた時もキャッチできるようにしてみましょう。 [VB.NET] Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As System.IO.FileNotFoundException 'FileNotFoundExceptionをキャッチした時 Console.WriteLine(ex.Message) Return String.Empty Catch ex As UnauthorizedAccessException 'UnauthorizedAccessExceptionをキャッチした時 Console.WriteLine(ex.Message) Return String.Empty End Try [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (System.IO.FileNotFoundException ex) { //FileNotFoundExceptionをキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } catch (UnauthorizedAccessException ex) { //UnauthorizedAccessExceptionをキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } このように、Catchブロックを幾つも追加することができます。 最後にExceptionをキャッチ上記のコードでは、FileNotFoundExceptionとUnauthorizedAccessExceptionしかキャッチできません。それ以外の例外がスローされたときは、全く例外処理を行わなかった時と同じようになってしまいます。そうならないように、最後にExceptionをキャッチするとよいでしょう。 [VB.NET] Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As System.IO.FileNotFoundException 'FileNotFoundExceptionをキャッチした時 Console.WriteLine(ex.Message) Return String.Empty Catch ex As UnauthorizedAccessException 'UnauthorizedAccessExceptionをキャッチした時 Console.WriteLine(ex.Message) Return String.Empty Catch ex As Exception 'それ以外の例外をキャッチした時 Console.WriteLine(ex.Message) Return String.Empty End Try [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (System.IO.FileNotFoundException ex) { //FileNotFoundExceptionをキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } catch (UnauthorizedAccessException ex) { //UnauthorizedAccessExceptionをキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } catch (Exception ex) { //それ以外の例外をキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } このようにすると、FileNotFoundExceptionでもUnauthorizedAccessExceptionでもない例外がスローされたときに、最後のCatchブロックが実行されるようになります。 なぜExceptionで全ての例外をキャッチできるかCatchにExceptionを指定すると全ての例外をキャッチできて、FileNotFoundExceptionを指定するとFileNotFoundExceptionだけをキャッチできると説明しましたが、もう少し詳しく説明しましょう。 Exceptionで全ての例外をキャッチできるのは、全ての例外クラスはExceptionクラスから派生しているからです。FileNotFoundExceptionクラスも、UnauthorizedAccessExceptionクラスも、その他の例外クラスもすべてExceptionクラスから派生しています。 そして、スローされた例外の型がCatchに指定された例外クラスまたはその基本クラスと一致すれば、そのCatchブロックが実行されます。 例えば、CatchにIOExceptionを指定すると、IOExceptionクラスから派生したPathTooLongException、DirectoryNotFoundException、FileNotFoundExceptionなどの例外をすべてキャッチできます。 [VB.NET] Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As System.IO.IOException 'PathTooLongException、DirectoryNotFoundException、 'FileNotFoundExceptionなど、IOExceptionから派生した例外を 'キャッチした時 Console.WriteLine(ex.Message) Return String.Empty Catch ex As Exception 'それ以外の例外をキャッチした時 Console.WriteLine(ex.Message) Return String.Empty End Try [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (System.IO.IOException ex) { //PathTooLongException、DirectoryNotFoundException、 //FileNotFoundExceptionなど、IOExceptionから派生した例外を //キャッチした時 Console.WriteLine(ex.Message); return string.Empty; } catch (Exception ex) { //それ以外の例外をキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } Catchの順番複数のCatchブロックがあるときにTryブロックで例外がスローされると、その例外の種類に合致するCatchを上から順番に検索し、見つかった初めのCatchブロックだけが実行されます。 例えば、Exceptionが指定されたCatchブロックを先頭に書いてしまったら、全ての例外がそのCatchブロックで処理されてしまうため、それ以降のCatchブロックは一切実行されなくなります。 [VB.NET] Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch ex As Exception '全ての例外をキャッチ Console.WriteLine(ex.Message) Return String.Empty Catch ex As System.IO.FileNotFoundException '実行されないコード Console.WriteLine(ex.Message) Return String.Empty Catch ex As UnauthorizedAccessException '実行されないコード Console.WriteLine(ex.Message) Return String.Empty End Try [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (Exception ex) { //全ての例外をキャッチ Console.WriteLine(ex.Message); return string.Empty; } catch (System.IO.FileNotFoundException ex) { //実行されないコード Console.WriteLine(ex.Message); return string.Empty; } catch (UnauthorizedAccessException ex) { //実行されないコード Console.WriteLine(ex.Message); return string.Empty; } 上記の例は、コンパイルエラー(「前の catch 句はこれ、またはスーパー型 ('System.Exception') の例外のすべてを既にキャッチしました。」)となります。 Exceptionオブジェクトが必要ないときCatchブロックでExceptionオブジェクトを取得する必要ないときは、次のように省略できます。 [VB.NET] Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) Catch '全ての例外をキャッチする Return String.Empty End Try [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch { //全ての例外をキャッチする return string.Empty; } また、C#では、次のようにして例外の種類だけを記述することもできます。 [C#] try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); } catch (System.IO.FileNotFoundException) { //FileNotFoundExceptionをキャッチする Console.WriteLine("ファイルが存在しません。"); return string.Empty; } catch { //FileNotFoundException以外の例外をキャッチする return string.Empty; } 確実にCloseメソッドを呼び出す今まではFile.OpenTextメソッドのみをTryブロックに入れていましたが、ファイルの読み込みでもエラーは発生しますので、ReadToEndメソッドもTryブロックに入れたほうがよいでしょう。ただしその場合、開いたファイルを確実に閉じるために、全てのCatchブロックでCloseメソッドを呼び出す必要があります。しかし通常はそのようなことはせず、Finallyブロックを使用します。 [VB.NET] Public Function ReadAllText(ByVal filePath As String) As String 'ファイルが存在しているか調べる If System.IO.File.Exists(filePath) Then Return String.Empty End If Dim sr As System.IO.StreamReader = Nothing Dim s As String Try 'filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath) '内容をすべて読み込む s = sr.ReadToEnd() Catch ex As Exception '例外をキャッチした時 Console.WriteLine(ex.Message) Return String.Empty Finally '確実にファイルを閉じる If Not (sr Is Nothing) Then '閉じる sr.Close() End If End Try '結果を返す Return s End Function [C#] public string ReadAllText(string filePath) { //ファイルが存在しているか調べる if (System.IO.File.Exists(filePath)) { return string.Empty; } System.IO.StreamReader sr = null; string s; try { //filePathをUTF-8コードとして開く sr = System.IO.File.OpenText(filePath); //内容をすべて読み込む s = sr.ReadToEnd(); } catch (Exception ex) { //例外をキャッチした時 Console.WriteLine(ex.Message); return string.Empty; } finally { //確実にファイルを閉じる if (sr != null) { //閉じる sr.Close(); } } //結果を返す return s; } Finallyブロックのコードは確実に実行されますので、上記のようにすれば、確実にファイルを閉じることができます。 さらにFinallyについて詳しくは、「Dispose、Closeが確実に呼び出されるようにする」をご覧ください。 例外をキャッチしなかった時エラーが発生した時にエラー処理を行わなかった時は、通常、「アプリケーションのコンポーネントで、ハンドルされていない例外が発生しました。...」というダイアログが表示されます。このダイアログを表示せずに、適切な処理を行う方法は、「捕捉されなかった例外がスローされたことを知る」で説明しています。 ここで説明しなかった事柄ここではエラー処理の基本のみを説明しました。決してこれが全てではありません。 例えば、例外の再スローについては説明しませんでした。VB.NETのCatchステートメントに付けるWhen句や、「On Error GoTo」を使った非構造化例外処理も説明しませんでした。これらについては、リファレンスやMSDNをご覧ください。 |
|
Copyright 2002-2008 DOBON!. All rights reserved.
|