| 優點 | 缺點 |
|---|---|
| 無需實例化,呼叫簡便 | 無法實作介面、不可多型 |
| 效能略優 (無 this、易內聯) | 靜態狀態生命週期 = 應用程式域,難釋放、難重置 |
| 適合無狀態工具邏輯 | 隱性全域狀態 → 多執行緒競爭、測試困難 |
| 單例/工廠模式常用 | 違反依賴反轉 (DIP),難以 Mock/替換實作 |
Math.Sin, String.IsNullOrEmpty, Path.Combine — 無副作用、無狀態、輸入完全決定輸出。public static string ToMd5(this string s) — 必須為靜態類別中的靜態方法。User.Create(...), DbContextOptionsBuilder — 封裝複雜建構邏輯。Logger.Instance, ConfigurationManager.AppSettings (舊版)。static decimal CalculateOrderTotal(Order o) 若內部存取靜態快取/設定 → 測試需重置全域狀態、平行測試互相干擾。static User GetUser(int id) 直接呼叫 ADO.NET/EF Core → 無法替換為 Mock/InMemory 單元測試。
// ❌ 靜態版本 - 難以測試
public static class OrderService
{
public static decimal CalculateTotal(int orderId)
{
var repo = new SqlOrderRepository(); // 硬編碼依賴
var order = repo.Get(orderId);
return order.Items.Sum(i => i.Price * i.Qty);
}
}
// ✅ 介面 + 實例版本 - 可注入、可測試
public interface IOrderRepository { Order Get(int id); }
public class SqlOrderRepository : IOrderRepository { ... }
public class OrderService
{
private readonly IOrderRepository _repo;
public OrderService(IOrderRepository repo) => _repo = repo;
public decimal CalculateTotal(int orderId)
=> _repo.Get(orderId).Items.Sum(i => i.Price * i.Qty);
}
// 單元測試
var mock = new Mock();
mock.Setup(r => r.Get(1)).Returns(new Order { Items = new[] { new Item { Price=10, Qty=2 } } });
var svc = new OrderService(mock.Object);
Assert.Equal(20, svc.CalculateTotal(1));
readonly、ConcurrentDictionary、Lazy<T> 並仔細處理執行緒安全。靜態方法本身非善非惡,**關鍵在於是否引入隱性共享狀態與硬編碼依賴**。守住「無狀態、無副作用、可純函式化」底線,即能安全享受其便利性。