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

C# 委託、事件與 Lambda 表達式 演進史:從函數指針到表達式樹

     編輯:關於C語言

委託 是 C# 將方法「一等公民化」的基石,事件 是基於委託的發布-訂閱標準化,Lambda 表達式 讓函數式編程風格無縫融入。本文梳理三者**從 .NET 1.0 到現代版本的演進脈絡**,剖析協變/逆變、匿名方法、表達式樹、async/await 等關鍵節點。

一、 核心類型層級關係


System.Delegate (abstract)
  └─ System.MulticastDelegate (abstract, sealed by runtime)
       ├─ 用戶定義委託類型 (編譯器生成密封類)
       │    ├─ Invoke()           // 同步調用
       │    ├─ BeginInvoke()      // APM 異步開始
       │    └─ EndInvoke()        // APM 異步結束
       └─ 內置委託
            ├─ Action / Action<T...>      // void 返回
            ├─ Func<TResult> / Func<T...,TResult> // 有返回值
            ├─ Predicate<T>              // Func<T,bool> 別名
            ├─ EventHandler / EventHandler<TEventArgs> // 標準事件模式
            └─ Comparison<T>             // 排序比較

二、 歷史版本演進時間軸

版本特性影響
.NET 1.0 (C# 1.0)委託、事件類型安全的函數指針、標準發布-訂閱模式
.NET 2.0 (C# 2.0)匿名方法 delegate { }內聯代碼塊,捕獲外部變量 (閉包雛形)、簡化單次回調
.NET 3.5 (C# 3.0)Lambda 表達式 =>、表達式樹 Expression<TDelegate>語法極簡、LINQ 基石、動態查詢/編譯、EDM 映射
.NET 4.0 (C# 4.0)委託協變/逆變 in/outFunc<in T, out TResult>、Action<in T> 類型安全轉換
.NET 4.5 (C# 5.0)async/await異步編程模式統一,Task 替代 BeginInvoke
C# 6.0+表達式體成員、nameof、空條件委託調用 ?.Invoke()語法糖提升可讀性與安全性
C# 9.0+函數指針 delegate*、靜態匿名函數 static () => 高性能互操作、避免閉包分配

三、 委託深度:多播、協變逆變、動態調用

1. 多播鏈表


Action a = () => Console.WriteLine("1");
a += () => Console.WriteLine("2");
a(); // 順序調用 1 → 2
// 內部: _invocationList 鏈表,異常中斷後續調用

2. 協變/逆變 (C# 4.0)


// 協變: 返回類型可隱式轉換為基類
Func<Dog> fDog = () => new Dog();
Func<Animal> fAnimal = fDog; // 合法: out TResult

// 逆變: 參數類型可隱式轉換為派生類
Action<Animal> aAnimal = a => Console.WriteLine(a);
Action<Dog> aDog = aAnimal; // 合法: in T

3. DynamicInvoke 通用調用


Delegate d = (Func<int,int>)(x => x * 2);
object result = d.DynamicInvoke(5); // 返回 10 (object)
// 適用: 插件系統、腳本引擎、運行時綁定

四、 事件標準模式與最佳實踐


// 1. 事件參數類
public class OddFoundEventArgs : EventArgs {
    public int Number { get; }
    public OddFoundEventArgs(int n) => Number = n;
}

// 2. 發布者
public class OddFinder {
    // 標準事件模式: EventHandler
    public event EventHandler<OddFoundEventArgs> OddFound;
    
    public void Find(int from, int to) {
        for (int i = from; i <= to; i++) {
            if (i % 2 != 0) {
                // 安全觸發: 拷貝委託引用防併發移除
                var handler = OddFound;
                handler?.Invoke(this, new OddFoundEventArgs(i));
            }
        }
    }
}

// 3. 訂閱者
var finder = new OddFinder();
finder.OddFound += (s, e) => Console.WriteLine($"發現奇數: {e.Number}");
finder.Find(1, 10);

關鍵點

  • event 關鍵字:限制外部只能 +=/-=,**不能直接賦值/調用**,保護封裝。
  • 觸發前**拷貝委託引用** (`var h = Event; h?.Invoke(...)`) 防止併發退訂導致 NullReference。
  • 異常處理:單個處理器異常會中斷後續調用,建議處理器內部 try-catch 或使用自定義多播邏輯。

五、 Lambda 表達式三大形態

形態語法編譯目標典型用途
表達式 Lambdax => x * 2委託 或 表達式樹 Expression<Func<int,int>>LINQ 查詢、動態構建邏輯、EDM 映射
語句 Lambdax => { var t=x*2; return t; }委託 (不可轉表達式樹)多行邏輯、副作用操作
異步 Lambdaasync x => await Fetch(x)Func<T, Task<TResult>>異步管道、並發處理

六、 表達式樹:代碼即數據


// 1. 創建表達式樹 (需 using System.Linq.Expressions)
Expression<Func<int, int, int>> expr = (a, b) => a + b;

// 2. 編譯為委託 (運行時生成 IL)
Func<int, int, int> compiled = expr.Compile();
int result = compiled(3, 5); // 8

// 3. 遍歷/修改樹 (動態查詢核心)
var binary = (BinaryExpression)expr.Body;
Console.WriteLine(binary.NodeType); // Add
Console.WriteLine(binary.Left);     // a
Console.WriteLine(binary.Right);    // b

應用:Entity Framework 將 Expression 翻譯為 SQL;動態篩選器構建器;規則引擎。

七、 現代高性能替代方案

場景傳統委託/Lambda現代替代 (C# 9+/11+)
熱路徑頻繁調用委託調用開銷 (間接跳轉、閉包分配)delegate* 函數指針 (非托管調用約定,零分配)
避免閉包捕獲捕獲變量 → 顯示類分配static () => ... 靜態匿名函數 (無 this、無捕獲)
異步回調BeginInvoke/EndInvoke (APM)async/await + ValueTask (TAP)
互操作回調Marshal.GetFunctionPointerForDelegatedelegate* unmanaged<...> 直接轉函數指針

委託賦予 C#「函數即值」的能力,事件建立了鬆耦合通信標準,Lambda 讓函數式思維落地,表達式樹打通「代碼與數據」邊界。掌握演進脈絡,才能在遺留維護與現代架構間游刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved