「好記性不如爛筆頭」。系統整理知識點、構建專屬知識體系,是從「看懂」到「會用」、「會教」的必經之路。本文以擴展方法為例,演示如何將傳統靜態工具類重構為現代流式 API,提升代碼可讀性與復用性。
早期為復用邏輯,常寫 PublicCenter.GetDaysMin(days) 這類靜態方法:
public class PublicCenter
{
public long GetDaysMin(int days) => days * 24 * 60;
}
// 調用
int days = 3;
int minutes = PublicCenter.GetDaysMin(days);
// 缺點:調用方需知道工具類名、方法名,IDE 無智能提示關聯性
public static class IntExtensions
{
public static long ToMinutes(this int days) => days * 24L * 60;
}
int days = 3;
// 傳統
int min1 = PublicCenter.GetDaysMin(days);
// 擴展方法(智能提示自動出現 ToMinutes)
int min2 = days.ToMinutes();
// 更自然:3.ToMinutes() 甚至 (3).ToMinutes()
擴展方法在 IL 層面不存在,編譯器將 days.ToMinutes() 重寫為 IntExtensions.ToMinutes(days)。這意味著:
this 引用傳遞開銷「擴展方法使您能夠向現有類型『添加』方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。」
關鍵詞:添加(非修改)、無需派生、不修改原類型 —— 完美符合開放封閉原則。
public static class OrderExtensions
{
public static Order EnsurePaid(this Order order)
{
if (order.Status != OrderStatus.Paid) throw new InvalidOperationException("訂單未支付");
return order; // 返回 this 支持鏈式
}
public static Order EnsureInStock(this Order order, IInventoryService inv)
{
if (!inv.HasStock(order.Items)) throw new InvalidOperationException("庫存不足");
return order;
}
public static Order Ship(this Order order, IShippingService shipping)
{
shipping.Ship(order);
order.Status = OrderStatus.Shipped;
return order;
}
}
// 業務代碼:一行完成驗證發貨閉環
order.EnsurePaid()
.EnsureInStock(inventory)
.Ship(shipping);
| 誤區 | 正確做法 |
|---|---|
| 擴展方法嘗試修改結構體字段 | 結構體為值類型,擴展方法接收副本,修改無效;改返回新值或改用 class |
| 濫用擴展核心業務邏輯 | 核心邏輯屬於類職責,擴展方法用於輔助、適配、流式 API |
| 忽略 null 檢查 | 擴展方法第一參數可為 null,內部必須防禦性編程 |
| 命名空間污染 | 按模塊組織,按需 using,避免全局污染 |
擴展方法將「操作數據的函數」轉化為「數據擁有的行為」,讓代碼閱讀順序與業務思維順序一致。從工具類到流式 API,不僅是語法升級,更是設計思維的轉變:以對象為中心,讓數據流動起來。