次の方法で共有


例外の作成とスロー

例外は、プログラムの実行中にエラーが発生したことを示すために使用されます。 エラーを記述する例外オブジェクトが作成され、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 インスタンスを作成する必要があります。 元の例外は、ArgumentException パラメーターとしてInnerExceptionのコンストラクターに渡す必要があります。

    static int GetValueFromArray(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    

    前の例は、 InnerException プロパティの使用方法を示しています。 意図的に簡略化されています。 実際には、インデックスを使用する前に、インデックスが範囲内にあることを確認する必要があります。 パラメーターのメンバーが、メンバーを呼び出す前に予測できなかった例外をスローした場合に、例外をラップするこの手法を使用できます。

例外には、 StackTraceという名前のプロパティが含まれています。 この文字列には、現在の呼び出し履歴のメソッドの名前と、メソッドごとに例外がスローされたファイル名と行番号が含まれます。 StackTrace オブジェクトは、throw ステートメントのポイントから共通言語ランタイム (CLR) によって自動的に作成されるため、スタック トレースを開始するポイントから例外をスローする必要があります。

すべての例外には、 Messageという名前のプロパティが含まれています。 この文字列は、例外の理由を説明するために設定する必要があります。 セキュリティに影響を受けやすい情報は、メッセージ テキストに含めてはいけません。 Messageに加えて、ArgumentExceptionには、例外がスローされる原因となった引数の名前に設定する必要がある ParamName という名前のプロパティが含まれています。 プロパティ セッターでは、 ParamNamevalue に設定する必要があります。

パブリックメソッドとプロテクトメソッドは、意図した機能を完了できない時には常に例外を投げます。 スローされる例外クラスは、エラー条件に適合する最も具体的な例外です。 これらの例外はクラス機能の一部として文書化する必要があり、派生クラスまたは元のクラスの更新は、下位互換性のために同じ動作を保持する必要があります。

例外をスローするときに避ける必要があること

次の一覧は、例外をスローするときに回避すべきことを示したものです。

  • 例外を使用して、通常の実行の一部としてプログラムのフローを変更しないでください。 例外を使用して、エラー状態を報告して処理します。
  • 例外をスローするのではなく、戻り値またはパラメーターとして返してはなりません。
  • 独自のソース コードから意図的に System.ExceptionSystem.SystemExceptionSystem.NullReferenceException、または System.IndexOutOfRangeException をスローしないでください。
  • デバッグ モードではスローできてもリリース モードではスローできない例外を作成してはなりません。 開発フェーズ中に実行時エラーを特定するには、代わりに Debug Assert を使用します。

タスクを返すメソッドの例外

async修飾子で宣言されたメソッドには、例外に関して特別な考慮事項がいくつかあります。 async メソッドでスローされた例外は、返されたタスクに格納され、タスクが待機になるまで発生しません。 格納されている例外の詳細については、「 非同期例外」を参照してください。

メソッドの非同期部分を入力する前に、引数を検証し、 ArgumentExceptionArgumentNullExceptionなどの対応する例外をスローすることをお勧めします。 つまり、これらの検証例外は、作業が開始される前に同期的に発生する必要があります。 次のコード スニペットは、例外がスローされた場合、 ArgumentException 例外が同期的に出現し、 InvalidOperationException が返されたタスクに格納される例を示しています。

// Non-async, task-returning method.
// Within this method (but outside of the local function),
// any thrown exceptions emerge synchronously.
public static Task<Toast> ToastBreadAsync(int slices, int toastTime)
{
    if (slices is < 1 or > 4)
    {
        throw new ArgumentException(
            "You must specify between 1 and 4 slices of bread.",
            nameof(slices));
    }

    if (toastTime < 1)
    {
        throw new ArgumentException(
            "Toast time is too short.", nameof(toastTime));
    }

    return ToastBreadAsyncCore(slices, toastTime);

    // Local async function.
    // Within this function, any thrown exceptions are stored in the task.
    static async Task<Toast> ToastBreadAsyncCore(int slices, int time)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        // Start toasting.
        await Task.Delay(time);

        if (time > 2_000)
        {
            throw new InvalidOperationException("The toaster is on fire!");
        }

        Console.WriteLine("Toast is ready!");

        return new Toast();
    }
}

例外クラスを定義する

プログラムは、定義済みの例外クラスを 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# の構文と使用法の決定的なソースです。

こちらも参照ください