[C#] Result Pattern - 在 .NET 寫出乾淨錯誤處理的一個方式

2025-06-04

在底層開發程式的時候我常看到一個關鍵字之 IsSuccess 、 Fail 、Error 在不同的 Service 中都有出現

我就在想是因為大家的寫作都是一樣的風格嗎,後來我才知道原來是一種 Result Pattern 的設計跟導入


Result Pattern 簡單的說有兩個很重要的狀態

成功 - Result.Success(value)

失敗 - Result.Fail("reason")


C# Code for Result Pattern :

public class Result<t> { public bool IsSuccess { get; } public string Error { get; } public T Value { get; } private Result(bool ok, T value, string error) { IsSuccess = ok; Value = value; Error = error; } public static Result<t> Success(T value) =&gt; new Result<t>(true, value, ""); public static Result<t> Fail(string error) =&gt; new Result<t>(false, default(T), error); } </t></t></t></t></t>


看起來很簡單,但是就可以讓很多錯誤訊息統一化

使用方式

C# Code:

public Result<user> CreateUser(string name, string email) { if (string.IsNullOrWhiteSpace(name)) return Result<user>.Fail("Name is required"); if (string.IsNullOrWhiteSpace(email)) return Result<user>.Fail("Email is required"); var user = new User(name, email); return Result<user>.Success(user); } </user></user></user></user>

這些都是預期內錯誤 不需要 try/catch


呼叫端寫法更乾淨

var result = CreateUser("Donma", ""); if (!result.IsSuccess) { Console.WriteLine("Error: " + result.Error); return; } Console.WriteLine("Created: " + result.Value.Name);

流程像在寫規格 不是一堆防呆與例外控制

遇到不可預期的錯誤怎麼辦

Result Pattern 不用來取代真正的 exception 它只處理 "預期內的錯誤"

不可預期的錯誤交給 exception

例如 DB 掛掉、Redis timeout、File I/O 堵塞、連外 API 掛掉、NullReferenceException、SDK 錯誤..等

這些才是 exception 的領域

正確方式是 Service 層用 try/catch 包住預期外錯誤 然後轉成 Result.Fail

public Result<user> SaveToDb(User user) { try { _db.Save(user); return Result<user>.Success(user); } catch (Exception ex) { return Result<user>.Fail("Database error: " + ex.Message); } } </user></user></user>


商業邏輯的錯誤用 Result 回傳 系統層級的不可預期錯誤用 try/catch 包起來 真正無法處理的 bug 就讓它 throw 給系統監控 這樣整個錯誤處理的分工會非常清楚 程式的流程也能更可預期 結構保持一致 不需要再依賴 null 呼叫端的寫法自然 也不會到處充滿 try/catch 說穿了就是方便 debug 還有測試 難怪我在看很多 open soruce 都這樣設計


當麻許的碎念筆記 2014 | Donma Hsu Design.