エラーの発生および処理のガイドライン
次の規則は、エラーの発生および処理のガイドラインを示しています。
例外を発生させる結果となるすべてのコード パスには、例外をスローすることなく正常に処理を続行できるかどうかを確認するためのメソッドを用意する必要があります。たとえば、FileNotFoundException を避けるには File.Exists を呼び出します。これは、常に可能な処置ではありませんが、通常の実行時に例外がスローされることをなくすことを目的としています。
Exception クラス名の末尾には、次の例に示すように、
Exception
** サフィックスを付けます。Public Class FileNotFoundException Inherits Exception ' Implementation code goes here. End Class [C#] public class FileNotFoundException : Exception { // Implementation code goes here. }
例外クラスを作成するときは、次のコード例に示す共通のコンストラクタを使用します。
Public Class XxxException Inherits ApplicationException Public Sub New() ' Implementation code goes here. End Sub Public Sub New(message As String) ' Implementation code goes here. End Sub Public Sub New(message As String, inner As Exception) ' Implementation code goes here. End Sub Public Sub New(info As SerializationInfo, context As StreamingContext) ' Implementation code goes here. End Sub End Class [C#] public class XxxException : ApplicationException { public XxxException() {... } public XxxException(string message) {... } public XxxException(string message, Exception inner) {... } public XxxException(SerializationInfo info, StreamingContext context) {...} }
ほとんどの場合は、定義済みの例外の型を使用します。新しい型の例外を定義するのは、クラス ライブラリを使用する開発者がその新しい例外型をキャッチし、その型自体に基づいてプログラムによるアクションを実行することが想定される場合だけに限ります。この方法は、例外文字列を解析する代わりに使用します。例外文字列を解析すると、パフォーマンスと保守性に悪影響を与えることになります。
たとえば、開発者が存在しないファイルを作成できるように、FileNotFoundException を定義しておくことは意味のあることです。ただし、FileIOException は、通常はコードで特別に処理される例外ではありません。
基本クラス SystemException から直接新しい例外を派生させないでください。System 名前空間で新しい例外を作成するときだけ、SystemException から継承します。その他の名前空間で新しい例外を作成するときは、ApplicationException から継承します。
SystemException または ApplicationException から派生させた新しい例外は名前空間ごとにグループ化します。たとえば、すべての System.IO 例外は IOException (SystemException から派生) の下でグループ化され、すべての Microsoft.Media 例外は MediaException (ApplicationException から派生) の下でグループ化できます。
各例外ではローカライズされた説明文字列を使用します。ユーザーに表示されるエラー メッセージは、スローされた例外の説明文字列から抽出されます。例外クラスからは抽出されません。
正しい文法と句読点を使用して、エラー メッセージを作成します。例外の説明文字列の文章は、句点で終了する必要があります。例外メッセージを汎用的に表示するコードでは、開発者が最後の句点を忘れた場合に対処する必要はありません。
プログラムによるアクセスを実現するために、例外プロパティを提供します。情報をプログラムによって追加することが役に立つ場合にだけ、説明文字列以外の情報を例外に含めます。ほとんどの場合は、例外に追加情報を含める必要はありません。
セキュリティで保護された情報は、例外メッセージで公開しないでください。ローカル ファイル システムのパスなどの情報は、権限が与えられた情報です。悪意のあるコードは、この情報を使用して、プライベートなユーザー情報をコンピュータから集めることができます。
通常のエラー、予期されるエラー、または正常な制御フローに対して例外を使用しないでください。
特に、一般的なエラーが発生した場合は、null を返すようにします。たとえば、File.Open コマンドは、ファイルが見つからない場合に null 参照を返しますが、ファイルがロックされている場合は例外をスローします。
通常に使用した場合には例外がスローされないようにクラスをデザインします。次のコード例の FileStream クラスは、開発者がファイルの末尾を超えて読み取ろうとしたときにスローされる例外を回避できるように、ファイルの末尾に達したかどうかを判断するための別の方法を公開しています。
Class FileRead Sub Open() Dim stream As FileStream = File.Open("myfile.txt", FileMode.Open) Dim b As Byte ' ReadByte returns -1 at end of file. While b = stream.ReadByte() <> true ' Do something. End While End Sub End Class [C#] class FileRead { void Open() { FileStream stream = File.Open("myfile.txt", FileMode.Open); byte b; // ReadByte returns -1 at end of file. while ((b = stream.ReadByte()) != true) { // Do something. } } }
プロパティ set アクセサまたはメソッドの呼び出しが、オブジェクトの現在の状態に対して不適切な場合には、InvalidOperationException 例外をスローします。
無効なパラメータが渡された場合、または検出された場合は、ArgumentException をスローするか、このクラスから派生させた例外を作成します。
スタック トレースは、例外が new 演算子で作成された場所ではなく、例外がスローされた場所から開始されます。例外をスローする場所を決定するときには、このことを考慮してください。
例外ビルダ メソッドを使用します。一般に、クラスはクラス実装内の複数の位置で同一の例外をスローします。コードの反復を防ぐために、new 演算子を使用して例外を作成し、これを返すヘルパ メソッドを使用します。ヘルパ メソッドの実装方法を次のコード例に示します。
class File { string fileName; public byte[] Read(int bytes) { if (!ReadFile(handle, bytes)) throw NewFileIOException(); } FileException NewFileIOException() { string description = // Build localized string, include fileName. return new FileException(description); } }
エラー コードや HRESULT を返す代わりに、例外をスローします。
できるだけ固有の例外をスローします。
例外に対して、開発者向けの意味のあるメッセージ テキストを作成します。
使用する例外のすべてのフィールドを設定します。
内部例外 (チェインされた例外) を使用します。ただし、補足情報を追加する場合や、例外の型を変更する場合以外は、例外をキャッチして再スローすることは避けてください。
NullReferenceException または IndexOutOfRangeException をスローするメソッドは作成しないでください。
プロテクト (ファミリ) メンバおよび内部 (アセンブリ) メンバでは引数チェックを実行します。プロテクト メソッドで引数チェックを実行しない場合は、その旨をドキュメントに明確に記述してください。特に記述がない場合は、引数チェックが実行されると見なされます。ただし、引数チェックを実行しない方が、パフォーマンスが向上することがあります。
例外をスローするときに副作用がある場合には、排除しておきます。関数から例外がスローされたときには、副作用が発生していないことを呼び出し元が前提とできるようにしておく必要があります。たとえば、Hashtable.Insert メソッドが例外をスローした場合、呼び出し元が、指定項目が Hashtable に追加されていないことを前提とできることが必要です。
標準の例外型
ランタイムが提供する標準例外と、派生クラスを作成する必要がある条件の一覧を次の表に示します。
例外の種類 | 基本型 | 説明 | 例 |
---|---|---|---|
Exception | Object | すべての例外の基本クラスです。 | なし (この例外の派生クラスを使用) |
SystemException | Exception | すべての実行時生成エラーの基本クラスです。 | なし (この例外の派生クラスを使用) |
IndexOutOfRangeException | SystemException | 配列のインデックスが誤っている場合にだけランタイムによりスローされます。 | 有効な範囲外で配列のインデックス付けを行った場合。
|
NullReferenceException | SystemException | null オブジェクトが参照された場合にだけランタイムによりスローされます。 | object o = null;
|
InvalidOperationException | SystemException | 無効な状態の場合にメソッドによりスローされます。 | 基になるコレクションから Item を削除した後で、Enumerator.GetNext() を呼び出した場合。 |
ArgumentException | SystemException | すべての引数例外の基本クラスです。 | なし (この例外の派生クラスを使用) |
ArgumentNullException | ArgumentException | null の引数を受け取らないメソッドによってスローされます。 | String s = null;
|
ArgumentOutOfRangeException | ArgumentException | 引数が特定の範囲内にあることを検査するメソッドによってスローされます。 | String s = "string";
|
ExternalException | SystemException | ランタイム以外の環境で発生する例外またはランタイム以外の環境を対象にした例外の基本クラスです。 | なし (この例外の派生クラスを使用) |
ComException | ExternalException | COM Hresult 情報をカプセル化する例外です。 | COM 相互運用機能で使用されます。 |
SEHException | ExternalException | Win32 構造化例外処理情報をカプセル化する例外です。 | アンマネージ コード相互運用機能で使用されます。 |
例外のラップ
コンポーネントと同じ層で発生するエラーは、対象の開発者にとって意味のある例外をスローする必要があります。TextReader クラスを使用してストリームからデータを読み取ろうとする開発者を対象にしたエラー メッセージを、次のコード例に示します。
Public Class TextReader
Public Function ReadLine() As String
Try
' Read a line from the stream.
Catch e As Exception
Throw New IOException("Could not read from stream", e)
End Try
End Function
End Class
[C#]
public class TextReader
{
public string ReadLine()
{
try
{
// Read a line from the stream.
}
catch (Exception e)
{
throw new IOException ("Could not read from stream", e);
}
}
}