擴充方法讓我們能在不修改原始碼、不建立衍生類別的情況下,為現有型別「添加」實例方法。本文從定義語法、編譯器轉換、優先順序規則、巢狀呼叫、衝突處理到實戰應用模式全方位解析。
public static class Extensions
{
public static void Foo(this string s) { /* ... */ }
}
// 使用
string s = "Hello";
s.Foo(); // 編譯器轉換為 Extensions.Foo(s)
本質: 實例方法呼叫語法糖,編譯期轉換為靜態方法呼叫。
.Where().OrderBy().Select()
public static class Extensions
{
// 單參數(擴充目標)
public static void Foo(this string s) { }
// 多參數
public static void Foo(this string s, int count, string prefix) { }
// 泛型擴充方法
public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
=> source == null || !source.Any();
}
class MyClass { public void Print() => Console.WriteLine("****"); }
static class MyExtensions { public static void Print(this MyClass s) => Console.WriteLine("haha"); }
MyClass o = new MyClass();
o.Print(); // 輸出 "****" —— 實例方法優先
IL 驗證:編譯器直接呼叫實例方法,完全忽略擴充方法。
using 優先
static class IntExtensions
{
public static int Test01(this int i) => i * 3;
public static int Test02(this int i) => i + 5;
}
int m = 7;
Console.WriteLine(m.Test01().Test02()); // 26 = (7*3)+5
Console.WriteLine(m.Test02().Test01()); // 36 = (7+5)*3
Console.WriteLine(IntExtensions.Test02(IntExtensions.Test01(m))); // 26
鏈式呼叫編譯為巢狀靜態方法呼叫,順序由左至右。
public interface IRepository<T> { void Add(T entity); }
public static class RepositoryExtensions
{
public static void AddRange<T>(this IRepository<T> repo, IEnumerable<T> entities)
{
foreach (var e in entities) repo.Add(e);
}
}
public static class ValidationExtensions
{
public static ValidationBuilder<T> Validate<T>(this T value) => new ValidationBuilder<T>(value);
}
public class ValidationBuilder<T>
{
private readonly T _value;
private readonly List _errors = new();
public ValidationBuilder<T> NotNull(string msg = "值不可為空")
{ if (_value == null) _errors.Add(msg); return this; }
public ValidationBuilder<T> Match(Func<T, bool> pred, string msg)
{ if (!pred(_value)) _errors.Add(msg); return this; }
public void ThrowIfInvalid()
{ if (_errors.Any()) throw new ValidationException(string.Join("; ", _errors)); }
}
// 使用
userInput.Validate().NotNull().Match(x => x.Length > 3, "長度需>3").ThrowIfInvalid();
public static class DateTimeExtensions
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek start = DayOfWeek.Monday)
{
int diff = (7 + (dt.DayOfWeek - start)) % 7;
return dt.AddDays(-diff).Date;
}
public static bool IsWeekend(this DateTime dt)
=> dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday;
}
MyApp.Extensions),按需 usingToString、GetHashCode)object, string, int),污染全域可見性