例外の作成とスロー
例外は、プログラムの実行中にエラーが発生したことを示すために使われます。 エラーを説明する例外オブジェクトが作成された後、throw
キーワードで "スロー" されます。 そのとき、ランタイムは最も互換性のある例外ハンドラーを検索します。
プログラマは、以下の条件が 1 つでも該当するときは、例外をスローする必要があります。
メソッドは、定義されている機能を完了できません。 たとえば、メソッドへのパラメーターに無効な値が設定されている場合などです。
static void CopyObject(SampleClass original) { _ = original ?? throw new ArgumentException("Parameter cannot be null", nameof(original)); }
オブジェクトの状態に基づくと、オブジェクトに対して行われた呼び出しが不適切です。 たとえば、読み取り専用ファイルに書き込もうとしたような場合です。 オブジェクトの状態により操作が許可されない場合は、InvalidOperationException のインスタンスまたはこのクラスの派生に基づくオブジェクトをスローします。 次のコードは、InvalidOperationException オブジェクトをスローするメソッドの例です。
public class ProgramLog { FileStream logFile = null!; public void OpenLog(FileInfo fileName, FileMode mode) { } public void WriteLog() { if (!logFile.CanWrite) { throw new InvalidOperationException("Logfile cannot be read-only"); } // Else write data to the log and return. } }
メソッドへの引数が原因で例外が発生しました。 この場合は、元の例外をキャッチして、ArgumentException のインスタンスを作成する必要があります。 元の例外は、InnerException パラメーターとして ArgumentException のコンストラクターに渡す必要があります。
static int GetValueFromArray(int[] array, int index) { try { return array[index]; } catch (IndexOutOfRangeException e) { throw new ArgumentOutOfRangeException( "Parameter index is out of range.", e); } }
注意
上記の例は説明を目的としています。 例外によるインデックスの検証は、ほとんどの場合、不適切な方法です。 例外は、上のような引数チェックではなく、プログラムの例外的な状態に対する保護のために使用する必要があります。
例外には、StackTrace というプロパティが含まれています。 この文字列には、現在の呼び出し履歴でのメソッドの名前と、各メソッドの例外がスローされたファイル名と行番号が含まれます。 スタック トレースを開始するポイントから例外をスローする必要があるため、共通言語ランタイム (CLR) によって throw
ステートメントのポイントから StackTrace オブジェクトが自動的に作成されます。
すべての例外には、Message というプロパティが含まれています。 例外の原因を説明するには、この文字列を設定する必要があります。 機密性の高い情報はメッセージ テキストに入れないようにする必要があります。 ArgumentException には、Message に加え、例外がスローされる原因となる引数の名前に設定される ParamName というプロパティが含まれています。 プロパティ セッターでは、ParamName は value
に設定する必要があります。
public と protected メソッドからは、意図された機能を完了できない場合、常に例外がスローされる必要があります。 スローされる例外クラスは、エラー状態に適合する使用可能な例外の中で最も具体的なものになります。 これらの例外はクラスの機能の一部として文書化する必要があり、派生クラスまたは元のクラスの更新では、旧バージョンとの互換性のために同じ動作を維持する必要があります。
例外をスローするときに避ける必要があること
次の一覧は、例外をスローするときに避ける必要があることです。
- プログラムのフローを変更するために、通常の実行の一部として例外を使用しないでください。 例外はエラー状態の報告と処理のために使用します。
- スローする代わりに、戻り値またはパラメーターとして例外を返さないでください。
- 独自のソース コードからは、意図的に System.Exception、System.SystemException、System.NullReferenceException、または System.IndexOutOfRangeException をスローしないでください。
- デバッグ モードではスローでき、リリース モードではスローできない例外は、作成しないでください。 開発フェーズ中に実行時エラーを識別するには、代わりにデバッグ アサートを使ってください。
例外クラスの定義
プログラムでは、System 名前空間で事前定義された例外クラスをスローするか (上記の場合を除きます)、Exception から派生することで独自の例外クラスを作成することができます。 派生クラスでは、少なくとも 3 つのコンストラクター (パラメーターなしのコンストラクター、メッセージ プロパティを設定するコンストラクター、Message および InnerException プロパティの両方を設定するコンストラクター) を定義する必要があります。 次に例を示します。
[Serializable]
public class InvalidDepartmentException : Exception
{
public InvalidDepartmentException() : base() { }
public InvalidDepartmentException(string message) : base(message) { }
public InvalidDepartmentException(string message, Exception inner) : base(message, inner) { }
}
新しいプロパティによって提供されるデータが例外の解決に役立つ場合は、それを例外クラスに追加します。 派生例外クラスに新しいプロパティを追加する場合は、ToString()
をオーバーライドして追加情報を返す必要があります。
C# 言語仕様
詳細については、「C# 言語仕様」の例外と throw ステートメントに関するセクションを参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。