傳統面向對象設計中,為現有類型添加功能只有兩條路:繼承(受限於密封類)或修改源碼(第三方庫不可改)。擴展方法提供第三條路:靜態語法糖實現的「開放封閉」增強,無需修改原類型、無需繼承、無運行期開銷。
public static class Extensions
{
// 基礎形式:擴展類型為第一參數,this 修飾
public static void Foo(this string s, string param) { ... }
// 泛型擴展
public static TResult Map(this TSource source, Func selector)
=> selector(source);
// 擴展介面(極其強大)
public static void ForEach(this IEnumerable source, Action action) { ... }
}
調用解析順序(高→低):
using 命名空間的擴展方法(越近優先級越高)namespace Demo
{
public class MyClass
{
public void Print() => Console.WriteLine("實例方法");
}
public static class MyExtensions
{
public static void Print(this MyClass c) => Console.WriteLine("擴展方法");
}
}
// 調用
var obj = new MyClass();
obj.Print(); // 輸出:實例方法
// 擴展方法被「隱藏」,除非顯式調用:MyExtensions.Print(obj);
結論:擴展方法永遠不會覆蓋實例方法,安全但需注意命名衝突導致的困惑。
public static class StringExtensions
{
public static string Truncate(this string s, int maxLength, string suffix = "...")
=> s.Length <= maxLength ? s : s.Substring(0, maxLength) + suffix;
}
// 用法
var title = "這是一個很長的標題需要截斷處理";
var shortTitle = title.Truncate(10); // "這是一個很..."
public static class QueryExtensions
{
public static IQueryable WhereIf(this IQueryable q, bool cond, Expression> p)
=> cond ? q.Where(p) : q;
public static IQueryable Page(this IQueryable q, int page, int size)
=> q.Skip((page-1)*size).Take(size);
}
// 用法:條件分頁查詢一氣呵成
var result = db.Users
.WhereIf(activeOnly, u => u.IsActive)
.WhereIf(!string.IsNullOrEmpty(keyword), u => u.Name.Contains(keyword))
.Page(pageIndex, pageSize)
.ToList();
// 第三方庫類型不可修改
public class LegacyUser { public string FullName { get; set; } public int YearsOld { get; set; } }
// 擴展適配為統一介面
public static class LegacyAdapters
{
public static UserDto ToDto(this LegacyUser u)
=> new UserDto { Name = u.FullName, Age = u.YearsOld };
}
// 業務代碼無感知差異
var dto = legacyUser.ToDto();
System 或 MyApp.Core.Extensions,專案級 using 全局可見System 下大量擴展,防止污染全局命名空間擴展方法是 C# 語法擴展性的里程碑,它將「為類型添加方法」從「修改類型定義」解耦出來。掌握優先級規則、善用鏈式模式、合理組織命名空間,能讓你構建出極具表達力的流式 API,讓代碼像讀英文一樣自然流暢。