首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 3.0 新體驗:擴充方法深度解析與實戰模式

     編輯:關於C#

擴充方法讓我們能在不修改原始碼、不建立衍生類別的情況下,為現有型別「添加」實例方法。本文從定義語法、編譯器轉換、優先順序規則、巢狀呼叫、衝突處理到實戰應用模式全方位解析。

定義與編譯器轉換


public static class Extensions
{
    public static void Foo(this string s) { /* ... */ }
}

// 使用
string s = "Hello";
s.Foo(); // 編譯器轉換為 Extensions.Foo(s)

本質: 實例方法呼叫語法糖,編譯期轉換為靜態方法呼叫。

為何使用擴充方法?

  • 發現性:IntelliSense 直接在型別上顯示,無需尋找工具類別
  • 擴充不可修改型別:BCL、第三方庫、介面
  • 流暢介面:鏈式呼叫 .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();
}

優先順序與衝突處理

情況一:同名實例方法 vs 擴充方法


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

鏈式呼叫編譯為巢狀靜態方法呼叫,順序由左至右。

實戰應用模式

1. 介面預設實作(C# 8.0 前)


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);
    }
}

2. 流暢驗證器


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();

3. 日期時間擴充


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),按需 using
  • ✅ 擴充介面而非具體類別,提升通用性
  • ✅ 方法名稱避免與 BCL 衝突(如 ToString、GetHashCode)
  • ❌ 避免擴充核心基礎型別(object, string, int),污染全域可見性
  • ❌ 不要用擴充方法取代領域模型核心行為
  • ⚠️ 擴充方法為編譯期技術,不支援多型覆寫,勿誤以為可被繼承類別重寫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved