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

.NET 委托演進史話:從緊耦合到異步事件的睡前故事

     編輯:關於.NET

第一幕:緊耦合 — 硬編碼回調

class Worker {
    public void Advise(Boss boss) { _boss = boss; }
    public void DoWork() {
        Console.WriteLine("工作開始");
        if (_boss != null) _boss.WorkStarted();
        Console.WriteLine("工作中...");
        if (_boss != null) _boss.WorkProgressing();
        Console.WriteLine("工作完成");
        if (_boss != null) {
            int grade = _boss.WorkCompleted();
            Console.WriteLine($"得分:{grade}");
        }
    }
    private Boss _boss;
}

class Boss {
    public void WorkStarted() { }
    public void WorkProgressing() { }
    public int WorkCompleted() { return 2; }
}

缺點:Worker 強依賴 Boss 具體類,無法擴展新監聽者 (如 Universe)。

第二幕:接口解耦 — 統一契約

interface IWorkerEvents {
    void WorkStarted();
    void WorkProgressing();
    int WorkCompleted();
}

class Worker {
    public void Advise(IWorkerEvents events) { _events = events; }
    // ... 調用 _events.xxx()
    private IWorkerEvents _events;
}

class Boss : IWorkerEvents { ... }
class Universe : IWorkerEvents { ... }

缺點:接口粒度粗,Boss 被迫實現不關心的方法 (空實現),違反接口隔離原則。

第三幕:委托細粒度 — 方法級訂閱

delegate void WorkStarted();
delegate void WorkProgressing();
delegate int WorkCompleted();

class Worker {
    public WorkStarted started;
    public WorkProgressing progressing;
    public WorkCompleted completed;
    public void DoWork() {
        Console.WriteLine("工作開始"); if (started != null) started();
        Console.WriteLine("工作中..."); if (progressing != null) progressing();
        Console.WriteLine("工作完成"); if (completed != null) { int g = completed(); Console.WriteLine($"得分:{g}"); }
    }
}

class Boss { public int WorkCompleted() { return 4; } }

class Universe {
    static void WorkerStartedWork() { Console.WriteLine("Universe 注意到..."); }
    static int WorkerCompletedWork() { return 7; }
    static void Main() {
        var p = new Worker();
        p.completed = new WorkCompleted(new Boss().WorkCompleted);
        p.started = new WorkStarted(Universe.WorkerStartedWork);
        p.completed = new WorkCompleted(Universe.WorkerCompletedWork); // 覆蓋 Boss!
        p.DoWork();
    }
}

缺點:public 委托字段可被任意覆蓋/直接調用 (破壞封裝),無法多播。

第四幕:事件封裝 — 安全的多播

class Worker {
    public event WorkStarted started;
    public event WorkProgressing progressing;
    public event WorkCompleted completed;
    // DoWork 內部調用不變
}

// 使用 += / -= 訂閱/退訂,不可直接賦值或調用
p.completed += new WorkCompleted(boss.WorkCompleted);
p.started += new WorkStarted(Universe.WorkerStartedWork);

event 關鍵字編譯器生成 `add_XXX`/`remove_XXX` 存取器,僅暴露 `+=`/`-=`。

第五幕:收獲所有結果 — GetInvocationList

if (completed != null) {
    foreach (WorkCompleted wc in completed.GetInvocationList()) {
        int grade = wc();
        Console.WriteLine($"得分:{grade}");
    }
}

第六幕:異步通知三部曲

1. Fire-and-Forget (BeginInvoke 無回調)

foreach (var wc in completed.GetInvocationList())
    wc.BeginInvoke(null, null); // 線程池執行,主線程立即返回

2. 輪詢等待 (BeginInvoke + IsCompleted)

foreach (var wc in completed.GetInvocationList()) {
    var ar = wc.BeginInvoke(null, null);
    while (!ar.IsCompleted) Thread.Sleep(1);
    int grade = wc.EndInvoke(ar);
    Console.WriteLine($"得分:{grade}");
}

3. 異步回調模式 (BeginInvoke + AsyncCallback)

foreach (var wc in completed.GetInvocationList())
    wc.BeginInvoke(new AsyncCallback(WorkGraded), wc);

private void WorkGraded(IAsyncResult ar) {
    var wc = (WorkCompleted)ar.AsyncState;
    int grade = wc.EndInvoke(ar);
    Console.WriteLine($"得分:{grade}");
}

主線程完全解放,完成時自動回調處理結果。

結局:各得其所

  • Boss/Universe:只關心感興趣的事件,無空實現負擔
  • Worker:異步通知不阻塞,結果通過回調收集
  • 架構:鬆耦合、可擴展、線程安全 (需注意跨線程調用 UI)
「他們永遠幸福地生活下去……直到下一個重構需求到來。」
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved