例外処理の実施

適切に記述されたエラー処理コード ブロックを使用すると、プログラムの堅牢性が強化され、アプリケーションでのエラー処理が原因でクラッシュする可能性が低減します。例外処理の実施に関する推奨事項を次に示します。

  • try ブロックおよび catch ブロックを設定する状況について理解します。たとえば、例外処理を実行せずに、発生する可能性のあるエラーの条件をプログラムによってチェックできます。その他の場合には、例外処理によってエラー条件をキャッチする方法が適切です。

    接続が閉じているかどうかをチェックする if ステートメントを次のコード例に示します。接続が閉じていない場合に例外をスローする方法の代わりに、この例のようにプログラムでチェックする方法を使用できます。

       If conn.State <> ConnectionState.Closed Then
          conn.Close()
       End If
       if(conn.State != ConnectionState.Closed)
          conn.Close();
    

    接続が閉じていない場合に例外をスローするコード例を次に示します。

       Try
          conn.Close()
       Catch ex As InvalidOperationException
          'Do something with the error or ignore it.
       End Try
       try {
         conn.Close();
       }
       catch(InvalidOperationException ex) {
         //Do something with the error or ignore it.
       }
    

    例外処理を使用するかどうかは、予期されるイベント発生頻度によって決まります。イベントが例外的であり、エラー (予期しないファイルの終端の検出など) である場合は、例外処理を使用する方法が有効です。これは、通常の場合は、実行されるコードが少ないためです。イベントが定期的に発生する場合は、プログラムによってエラーをチェックする方法が適切です。これは、例外が発生した場合に、例外を処理する時間がかかるためです。

  • 例外が発生する可能性のあるコードの前後に try ブロックと finally ブロックを配置し、catch ステートメントを 1 か所にまとめて配置します。このように配置することで、try ステートメントで例外が発生すると、finally ステートメントがリソースを閉じるかまたは解放し、catch ステートメントが中央の 1 か所から例外を処理します。

  • catch ブロックでは、特定の例外から一般的な例外の順に例外を配置します。この手法により、まず特定の例外が処理され、次にこの例外がより一般的な catch ブロックに渡されます。

  • 例外クラス名の終わりに "Exception" という単語を付けてください。たとえば、次のように指定します。

    Public Class EmployeeListNotFoundException
        Inherits Exception
    public class MyFileNotFoundException : Exception {
    }
    
  • ユーザー定義例外を作成するときには例外のメタデータを使用できるようにしてください。アプリケーション ドメイン間で例外が発生した場合や、リモート処理で必要となります。たとえば、アプリケーション ドメイン A によってアプリケーション ドメイン B が作成され、このドメイン B で例外をスローするコードが実行されるとします。アプリケーション ドメイン A で例外を適切にキャッチして処理するには、ドメイン A が、ドメイン B によりスローされた例外が格納されているアセンブリを検出できる必要があります。アプリケーション ドメイン B がスローした例外が、ドメイン A のアプリケーション ベースではなくドメイン B のアプリケーション ベースにあるアセンブリに格納されている場合には、ドメイン A は例外を検出できないため、共通言語ランタイムが FileNotFoundException をスローします。このような状況を回避するには、例外情報が格納されているアセンブリを次のいずれかの方法で配置できます。

    • 2 つのアプリケーション ドメインが共有する共通アプリケーション ベースにアセンブリを配置する。

      または

    • ドメインが共通アプリケーション ベースを共有していない場合には、例外情報が格納されているアセンブリに厳密な名前で署名し、グローバル アセンブリ キャッシュにこのアセンブリを配置する。

  • C# と C++ で独自の例外クラスを作成するときには、3 つ以上の共通コンストラクタを使用します。例については、「ユーザー定義例外の使用」を参照してください。

  • ほとんどの場合、事前に定義されている例外を使用します。プログラミング用途に限り、新しい例外の種類を定義します。新しい例外クラスを導入して、プログラマがこの新しい例外クラスに基づいて異なる処理をコーディングできるようにします。

  • ほとんどのアプリケーションでは、Exception クラスからカスタム例外を派生します。本来、カスタム例外は ApplicationException クラスから派生しなければならないと考えられていましたが、実際には、このことで大きな価値が付加されたことはないようです。

  • すべての例外に、ローカライズした説明文字列を含めます。ユーザーに対して表示されるエラー メッセージは、例外クラスではなく、スローされた例外の説明文字列から派生されます。

  • 文法的に正しいエラー メッセージを使用します。句点も含めてください。例外の説明文字列の文の末尾には、必ず句点を使用します。

  • プログラムによってアクセスできるように Exception のプロパティを指定します。説明文字列以外の追加情報を例外に含めるのは、プログラミングの点で追加情報が役立つ場合に限定してください。

  • 非常に一般的なエラーの場合には null を返します。たとえば、ファイルが見つからない場合には File.Open は null を返しますが、ファイルがロックされている場合には例外を返します。

  • 通常の使用状態では例外が返されないようにクラスをデザインします。たとえば、FileStream クラスは、ファイルの終端に到達したかどうかを判別するための例外を使用しない方法を公開します。これにより、ファイルの終端を越えて読み取りを実行しようとした場合にも例外がスローされません。ファイルの終端の読み取り方法を示すコード例を次に示します。

    Class FileRead
       Sub Open()
          Dim stream As FileStream = _
                        File.Open("myfile.txt", FileMode.Open)
            Dim b As Byte
            Dim result As Integer
            ' ReadByte returns -1 at EOF.
            Do 
               result = stream.ReadByte()
               If result = -1 Then Exit Do
                b = CByte(result)
                ' Do something.
            Loop
            ' Call stream.Close() here or in another method.
        End Sub 'Open
    End Class 'FileRead
    class FileRead {
        public void Open() {
            FileStream stream = File.Open("myfile.txt", FileMode.Open);
            byte b;
            int result;
            // ReadByte returns -1 at EOF.
            while ((result = stream.ReadByte()) != -1) {
                b = (byte)result;
                // Do something.
                }
            // Call stream.Close() here or in another method.
        }
    }
    
  • オブジェクトの現在の状態に対して、プロパティ セットまたはメソッドの呼び出しが適切でない場合には、InvalidOperationException をスローします。

  • 不正なパラメータが渡された場合には、ArgumentException または ArgumentException の派生クラスをスローします。

  • 例外がスローされたステートメントからスタック トレースが開始され、例外をキャッチしたステートメントでトレースが終了します。throw ステートメントを配置する位置を決定するときには、このことに注意してください。

  • 例外ビルダ メソッドを使用します。一般に、クラスはクラス実装内の複数の位置で同一の例外をスローします。コードが長くなることを防ぐため、例外を作成して返すヘルパ メソッドを使用します。たとえば、次のように指定します。

    Class File
       Private fileName As String
    
       Public Function Read(bytes As Integer) As Byte()
          If Not ReadFile(handle, bytes) Then
             Throw NewFileIOException()
          End If
       End Function 'Read
    
       Function NewFileIOException() As FileException
          Dim description As String = __unknown ' Build localized string, including fileName.
          Return New FileException(description) '
       End Function 'NewFileIOException
    End Class 'File
    class File {
        string fileName;
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
        FileException NewFileIOException() {
            string description = // Build localized string, including fileName.
            return new FileException(description);
         }
    }
    

    例外のコンストラクタを使用して例外を作成することもできます。これは ArgumentException などのグローバル例外クラスの場合に適切な方法です。

  • エラー コードや HRESULT を返す代わりに、例外をスローします。

  • 例外をスローするときに、中間結果を削除します。呼び出し元が、メソッドから例外がスローされるときに副作用が発生しないと仮定できる必要があります。

参照

その他の技術情報

例外の処理とスロー