擴充方法是 **static 類別的 static 方法**,第一個參數加上 this 修飾詞,即可讓該型別的實例「像呼叫實例方法一樣」呼叫它。本質是 **編譯器語法糖**,CLR 看到的仍是靜態方法呼叫。
public static class StringExtensions
{
public static int ToInt32(this string s) // this 修飾第一個參數
{
int.TryParse(s, out int result);
return result;
}
}
// 使用
string input = "42";
int value = input.ToInt32(); // 實例方法語法
// 等同於
int value2 = StringExtensions.ToInt32(input); // 靜態方法語法
// 擴充方法定義端:加上 ExtensionAttribute
.method public hidebysig static int32 ToInt32(string s) cil managed
{
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// ... 方法本體
}
// 呼叫端:編譯為靜態方法呼叫
IL_0007: ldloc.0 // s
IL_0008: call int32 StringExtensions::ToInt32(string)
public interface IMath { int Add(int x, int y); }
public static class MathExtensions
{
public static int Subtract(this IMath math, int x, int y) => x - y;
public static int Multiply(this IMath math, int x, int y) => x * y;
}
public class Calculator : IMath
{
public int Add(int x, int y) => x + y;
}
// 使用
IMath calc = new Calculator();
Console.WriteLine(calc.Add(1, 2)); // 介面原有方法
Console.WriteLine(calc.Subtract(10, 3)); // 擴充方法
Console.WriteLine(((IMath)calc).Multiply(4, 5)); // 介面轉型後也可用
※ 無法直接在介面實例上呼叫 (介面不可實例化),只能透過實作類別實例。
using 可見的)using 順序不影響,以「最近包含的命名空間」為準)。| 陷阱 | 現象 | 對策 |
|---|---|---|
| Null 參考 | null.ToInt32() 不會拋 NullReferenceException,進入方法內 s 為 null | 方法內部檢查 if (s == null) throw... 或回傳預設值 |
| 命名空間污染 | using 過多導致非預期擴充方法出現 | 將擴充方法放在專屬命名空間 (如 MyApp.Extensions),按需 using |
| 簽名衝突 | 實例方法與擴充方法同名同參數 | 實例方法永遠優先;避免為系統型別 (string, List<T>) 定義常見名稱擴充 |
| 值型別拷貝 | this StructType s 傳值,方法內修改不影響原值 | 改用 ref this StructType s (C# 7.2+) 或回傳新值 |
| 泛型型別推斷 | this IEnumerable<T> src 呼叫時 T 自動推斷 | 正常運作;複雜情況需顯式指定 src.Ext<ConcreteType>() |
System.Linq.Enumerable:Where, Select, OrderBy, FirstOrDefault…System.IO.StreamExtensions (CoreFX):**CopyToAsync**, ReadAllTextAsyncMicrosoft.Extensions.DependencyInjection:**AddScoped**, GetRequiredServiceMyProject.Extensions,避免全域污染。this 狀態,回傳新值;需修改狀態請改為實例方法。/// ,IntelliSense 會顯示藍色下箭頭圖示區分擴充方法。擴充方法以 ExtensionAttribute 標記靜態方法,提供「不修改原型別、不繼承、不包裝」的擴充能力,是 LINQ、ASP.NET Core、EF Core 等現代 .NET 框架流暢 API 設計的基石。善用介面擴充、優先順序規則與命名空間隔離,能構建乾淨、可發現、型別安全的擴充生態系。