跳转到内容

C Sharp 多播委托

来自代码酷
Admin留言 | 贡献2025年4月29日 (二) 18:41的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)


多播委托(Multicast Delegate)是C#中一种特殊的委托类型,能够同时引用多个方法,并按顺序调用它们。它是事件处理系统和观察者模式的核心实现机制。

基本概念[编辑 | 编辑源代码]

在C#中,委托本质上是类型安全的函数指针。多播委托则通过组合多个委托实例,形成一个调用列表(invocation list)。当多播委托被调用时,列表中的所有方法会按照添加顺序依次执行。

与单播委托的区别[编辑 | 编辑源代码]

  • 单播委托:仅能绑定一个方法
  • 多播委托:通过+=-=运算符组合多个方法

语法与使用[编辑 | 编辑源代码]

多播委托使用System.DelegateCombineRemove方法(或对应的运算符)来管理调用列表。

// 声明委托
public delegate void MessageHandler(string message);

class Program
{
    static void Main()
    {
        // 创建委托实例
        MessageHandler handler = ShowMessage;
        
        // 多播委托组合
        handler += FormatMessage;
        handler += LogMessage;
        
        // 调用多播委托
        handler("Hello World");
        
        // 移除委托
        handler -= LogMessage;
        handler("After removal");
    }
    
    static void ShowMessage(string msg) => Console.WriteLine($"显示: {msg}");
    static void FormatMessage(string msg) => Console.WriteLine($"格式化: {msg.ToUpper()}");
    static void LogMessage(string msg) => Console.WriteLine($"日志: {DateTime.Now}: {msg}");
}

输出结果:

显示: Hello World
格式化: HELLO WORLD
日志: 2023-11-15 10:00:00: Hello World
显示: After removal
格式化: AFTER REMOVAL

执行流程[编辑 | 编辑源代码]

多播委托的执行遵循以下规则: 1. 按添加顺序同步执行 2. 如果委托有返回值,只返回最后一个方法的返回值 3. 如果任一方法抛出异常,后续方法不会执行

sequenceDiagram participant Caller participant Delegate participant Method1 participant Method2 participant Method3 Caller->>Delegate: 调用委托 Delegate->>Method1: 执行方法1 Method1-->>Delegate: 完成 Delegate->>Method2: 执行方法2 Method2-->>Delegate: 完成 Delegate->>Method3: 执行方法3 Method3-->>Delegate: 完成 Delegate-->>Caller: 完成调用

数学表示[编辑 | 编辑源代码]

多播委托可以表示为方法的有序集合: MDelegate={f1,f2,...,fn} 调用时相当于函数组合: MDelegate(x)=fn(...(f2(f1(x)))

实际应用案例[编辑 | 编辑源代码]

事件处理系统[编辑 | 编辑源代码]

多播委托是C#事件的基础实现:

public class Button
{
    public event EventHandler Clicked;
    
    public void SimulateClick()
    {
        Clicked?.Invoke(this, EventArgs.Empty);
    }
}

class Program
{
    static void Main()
    {
        Button btn = new Button();
        btn.Clicked += OnButtonClick1;
        btn.Clicked += OnButtonClick2;
        
        btn.SimulateClick();
    }
    
    static void OnButtonClick1(object sender, EventArgs e) 
        => Console.WriteLine("按钮点击处理1");
        
    static void OnButtonClick2(object sender, EventArgs e) 
        => Console.WriteLine("按钮点击处理2");
}

中间件管道[编辑 | 编辑源代码]

在ASP.NET Core等框架中,多播委托用于构建请求处理管道:

public delegate Task RequestDelegate(HttpContext context);

public class MiddlewarePipeline
{
    private RequestDelegate _pipeline;
    
    public void Configure()
    {
        _pipeline = LogRequest;
        _pipeline += Authenticate;
        _pipeline += ProcessRequest;
    }
    
    public async Task Run(HttpContext context) => await _pipeline(context);
    
    // 各中间件方法...
}

高级主题[编辑 | 编辑源代码]

委托组合[编辑 | 编辑源代码]

可以使用Delegate.Combine显式组合委托:

Delegate d1 = new MessageHandler(ShowMessage);
Delegate d2 = new MessageHandler(FormatMessage);
Delegate combined = Delegate.Combine(d1, d2);

异常处理[编辑 | 编辑源代码]

建议在多播委托调用时处理异常:

foreach(Delegate handler in multicastDelegate.GetInvocationList())
{
    try {
        handler.DynamicInvoke(args);
    }
    catch(Exception ex) {
        // 处理异常
    }
}

最佳实践[编辑 | 编辑源代码]

  • 避免在多播委托中使用有返回值的方法
  • 考虑使用GetInvocationList()单独调用每个委托
  • 事件处理中应检查委托是否为null
  • 注意内存泄漏问题,及时移除不需要的委托

性能考虑[编辑 | 编辑源代码]

多播委托调用比直接方法调用稍慢,因为需要: 1. 遍历调用列表 2. 进行额外的类型检查 3. 可能需要装箱/拆箱操作

对于性能敏感场景,可以考虑缓存委托实例或使用其他设计模式。