本文へスキップ
BecomeCoder

C#デザインパターンコース · 第3章 現代の設計パターン · レッスン27

Result / Option ― 例外に頼らない誤りの表現

ブラウザで完結

導入

「見つからなかった」「入力が不正」——これらは想定内の失敗です。想定内の失敗まで例外で表すと、呼び出し側は毎回 try-catch を強いられます。Result / Option パターンは、成功か失敗かを戻り値の型で表し、呼び出し側に処理を促します。

図解

flowchart LR
    F["Divide(10, 0)"] --> R["Result<int>"]
    R -->|IsSuccess| S["Value を使う"]
    R -->|!IsSuccess| E["Error を表示"]
    style R fill:#e1f5fe

サンプル

Print(Divide(10, 2));   // 成功: 5
Print(Divide(10, 0));   // 失敗: ゼロ除算

void Print(Result<int> r) =>
    Console.WriteLine(r.IsSuccess ? $"成功: {r.Value}" : $"失敗: {r.Error}");

Result<int> Divide(int a, int b) =>
    b == 0 ? Result<int>.Fail("ゼロ除算") : Result<int>.Ok(a / b);

class Result<T>
{
    public bool IsSuccess { get; }
    public T? Value { get; }
    public string Error { get; }
    private Result(bool ok, T? value, string error) { IsSuccess = ok; Value = value; Error = error; }
    public static Result<T> Ok(T value) => new(true, value, "");
    public static Result<T> Fail(string error) => new(false, default, error);
}
  • 想定内の失敗は例外ではなく戻り値の型で表す
  • 呼び出し側は IsSuccess を見て、成功なら Value、失敗なら Error を扱う
  • 「失敗の可能性」が型に現れるので、扱い忘れを防げる(Option は「値があるか無いか」版)

やってみよう

Divide の失敗理由を変えたり、負の入力を弾く条件を足したりして、Result に載る Error の内容を確かめましょう。呼び出し側の分岐はそのまま使えます。

演習

class Result<T>
{
    public bool IsSuccess { get; }
    public T? Value { get; }
    private Result(bool ok, T? value) { IsSuccess = ok; Value = value; }
    public static Result<T> Ok(T value) => new(true, value);
    public static Result<T> Fail() => new(false, default);
}

// TODO: 空文字なら Fail、それ以外は長さを Ok で返す Parse を書いてください
Result<int> Parse(string s) => ___;

var r = Parse("hello");
Console.WriteLine(r.IsSuccess ? r.Value : -1);   // 5
  • 期待される出力: 5
ヒント1を見る

string.IsNullOrEmpty(s) ? Result<int>.Fail() : Result<int>.Ok(s.Length)

ヒント2を見る

“hello” は長さ5なので Ok(5) が返り、IsSuccess は true になります

まとめ

  • Result/Optionは成功・失敗を戻り値の型で表す
  • 想定内の失敗を例外にせず、扱い忘れを型で防ぐ
  • 呼び出し側は分岐で成功値・失敗理由を扱う

次回: やってはいけない設計=アンチパターンと、パターンの使いどころです。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

C# — ブラウザ内で実行

ブラウザ内でC#を動かす環境を読み込みます(初回のみ数秒)。
スクロールして表示された時点でも自動で読み込まれます。