C# 3.0 引入擴充方法,主要為 LINQ 服務,但也廣泛用於為現有型別(含第三方封閉原始碼型別)新增功能。本文深入解析定義規則、呼叫機制、名稱衝突優先順序、巢狀呼叫原理與 IL 層面真相。
internal 或 public)this,型別即為「被擴充的型別」namespace Hongjun.Guo
{
static class MyExtensionMethods
{
// 擴充 object 型別的 Print 方法
internal static void Print(this object s)
{
Console.WriteLine(s);
}
}
}
// 使用端
using Hongjun.Guo;
static void Main()
{
object o = "dsdgs";
o.Print(); // 像實例方法一樣呼叫
}
結論:原生方法優先,擴充方法被忽略,編譯不報錯。
// 第三方元件 v1:無 Print 方法
public class MyClass { }
// 我們為它擴充 Print
static class MyExtensionMethods
{
internal static void Print(this MyClass s)
{
Console.WriteLine("haha " + s.ToString());
}
}
// 第三方元件 v2:新增原生 Print 方法
public class MyClass
{
public void Print() { Console.WriteLine("****"); }
}
MyClass o = new MyClass();
o.Print(); // 輸出:**** (呼叫原生方法)
編譯器處理邏輯:
using 的命名空間,尋找符合的擴充方法(靜態類別的靜態方法)關鍵點:擴充方法在 IL 中不存在實例方法,只是編譯器語法糖,編譯後轉為靜態方法呼叫。
static class MyExtensionMethods
{
public static int Test01(this int i) => i * 3;
public static int Test02(this int i) => i + 5;
}
static void Main()
{
int mm = 7;
Console.WriteLine(mm.Test01().Test02()); // 26
Console.WriteLine(mm.Test02().Test01()); // 36
Console.WriteLine(MyExtensionMethods.Test02(MyExtensionMethods.Test01(mm))); // 26
}
| 呼叫式 | 等價靜態呼叫 | 計算過程 | 結果 |
|---|---|---|---|
mm.Test01().Test02() | Test02(Test01(mm)) | (7×3)+5 | 26 |
mm.Test02().Test01() | Test01(Test02(mm)) | (7+5)×3 | 36 |
Test02(Test01(mm)) | 同上 | (7×3)+5 | 26 |
巢狀呼叫會被編譯器「展開」為靜態方法巢狀呼叫,執行順序由內而外,與一般方法鏈結相同。
[Extension] 或 XML 註解說明為擴充方法擴充方法是 C# 強大的語法糖,本質是編譯期將實例呼叫轉換為靜態方法呼叫。理解其解析優先順序(原生 > 擴充)與IL 層面本質,能避免疑難雜症,並設計出優雅的流暢介面。