Administrator
发布于 2025-10-08 / 1 阅读
0
0

C# 8.5 委托(Delegate)

对于C#初学者,理解委托可以从以下几个简单直观的角度入手:

一、用生活中的例子理解委托

1. "遥控器"比喻

// 委托就像遥控器上的按钮
public delegate void 遥控器按钮();  // 定义一个委托类型
​
public class 电视机
{
    public void 开机()
    {
        Console.WriteLine("电视机开机了!");
    }
}
​
public class 空调
{
    public void 开机()
    {
        Console.WriteLine("空调开始制冷!");
    }
}
​
// 使用
电视机 我的电视 = new 电视机();
空调 我的空调 = new 空调();
​
遥控器按钮 电源按钮 = 我的电视.开机;  // 设置按钮功能
电源按钮();  // 按下按钮 → 电视开机
​
电源按钮 = 我的空调.开机;  // 重新设置按钮功能
电源按钮();  // 按下按钮 → 空调开机

2. "餐厅点菜"比喻

// 委托就像服务员,把顾客的订单交给厨师
public delegate void 厨师委托(string 菜名);  // 定义委托
​
public class 厨师
{
    public void 做川菜(string 菜名)
    {
        Console.WriteLine($"川菜厨师做:{菜名}");
    }
    
    public void 做粤菜(string 菜名)
    {
        Console.WriteLine($"粤菜厨师做:{菜名}");
    }
}
​
// 服务员(委托)连接顾客和厨师
厨师 张师傅 = new 厨师();
厨师委托 服务员 = 张师傅.做川菜;
​
服务员("麻婆豆腐");  // 输出:川菜厨师做:麻婆豆腐
服务员 = 张师傅.做粤菜;
服务员("白切鸡");    // 输出:粤菜厨师做:白切鸡

二、委托的核心思想

简单理解:

"委托是一种类型,它可以引用方法,让方法像变量一样被传递和调用"

三、从方法调用到委托的演进

阶段1:直接方法调用(硬编码)

public class 计算器
{
    public int 加(int a, int b)
    {
        return a + b;
    }
    
    public int 乘(int a, int b)
    {
        return a * b;
    }
}
​
// 使用:必须明确知道调用哪个方法
计算器 计算 = new 计算器();
int 结果1 = 计算.加(5, 3);  // 直接调用加法
int 结果2 = 计算.乘(5, 3);  // 直接调用乘法

阶段2:使用委托(灵活调用)

// 1. 定义委托类型
public delegate int 计算委托(int a, int b);
​
public class 计算器
{
    public int 加(int a, int b) { return a + b; }
    public int 乘(int a, int b) { return a * b; }
    public int 减(int a, int b) { return a - b; }
}
​
// 使用:通过委托动态选择方法
计算器 计算 = new 计算器();
计算委托 当前计算 = 计算.加;  // 委托指向加法方法
​
int 结果 = 当前计算(5, 3);    // 8
当前计算 = 计算.乘;           // 改为指向乘法方法
结果 = 当前计算(5, 3);        // 15

四、委托的基本语法

1. 委托声明

// 声明委托类型(定义方法签名)
public delegate void 简单委托();                    // 无参数,无返回值
public delegate int 计算委托(int x, int y);         // 有参数,有返回值
public delegate string 格式化委托(string 输入);     // 有参数,有返回值

2. 委托实例化和使用

public class 工具类
{
    public void 问好()
    {
        Console.WriteLine("你好!");
    }
    
    public int 平方(int x)
    {
        return x * x;
    }
}
​
// 使用委托
工具类 工具 = new 工具类();
​
// 实例化委托
简单委托 我的委托 = 工具.问好;
计算委托 计算委托 = 工具.平方;
​
// 调用委托
我的委托();                    // 输出:你好!
int 结果 = 计算委托(5);       // 返回:25

五、委托的高级特性

1. 多播委托(一个委托多个方法)

public delegate void 通知委托(string 消息);
​
public class 通知系统
{
    public void 发送邮件(string 消息)
    {
        Console.WriteLine($"邮件通知:{消息}");
    }
    
    public void 发送短信(string 消息)
    {
        Console.WriteLine($"短信通知:{消息}");
    }
    
    public void 记录日志(string 消息)
    {
        Console.WriteLine($"日志记录:{消息}");
    }
}
​
// 使用多播委托
通知系统 系统 = new 通知系统();
通知委托 通知器 = 系统.发送邮件;
​
// 添加多个方法到委托
通知器 += 系统.发送短信;  // 使用 += 添加方法
通知器 += 系统.记录日志;
​
// 调用时,所有方法都会执行
通知器("系统维护通知");
​
// 输出:
// 邮件通知:系统维护通知
// 短信通知:系统维护通知  
// 日志记录:系统维护通知
​
// 移除方法
通知器 -= 系统.发送短信;  // 使用 -= 移除方法

2. 匿名方法

计算委托 平方计算 = delegate(int x) 
{
    return x * x;
};
​
// 调用
int 结果 = 平方计算(5);  // 25

3. Lambda表达式(更简洁)

// 使用Lambda表达式创建委托
计算委托 加法 = (a, b) => a + b;
计算委托 乘法 = (a, b) => a * b;
​
// 调用
int 和 = 加法(3, 4);    // 7
int 积 = 乘法(3, 4);    // 12

六、实际应用场景

场景1:按钮点击事件

// 定义按钮类
public class 按钮
{
    // 定义点击事件(本质上是委托)
    public event Action 点击事件;
    
    public void 被点击()
    {
        Console.WriteLine("按钮被点击了!");
        // 触发所有注册的方法
        点击事件?.Invoke();
    }
}
​
public class 游戏界面
{
    public void 开始游戏()
    {
        Console.WriteLine("游戏开始!");
    }
    
    public void 播放音效()
    {
        Console.WriteLine("播放点击音效");
    }
}
​
// 使用
按钮 开始按钮 = new 按钮();
游戏界面 界面 = new 游戏界面();
​
// 注册多个事件处理方法
开始按钮.点击事件 += 界面.开始游戏;
开始按钮.点击事件 += 界面.播放音效;
​
// 点击按钮,触发所有注册的方法
开始按钮.被点击();

场景2:排序算法的灵活应用

// 定义比较委托
public delegate bool 比较委托(int a, int b);
​
public class 排序工具
{
    public void 排序(int[] 数组, 比较委托 比较方法)
    {
        // 使用委托来决定排序规则
        for (int i = 0; i < 数组.Length - 1; i++)
        {
            for (int j = i + 1; j < 数组.Length; j++)
            {
                if (比较方法(数组[i], 数组[j]))
                {
                    // 交换元素
                    int 临时 = 数组[i];
                    数组[i] = 数组[j];
                    数组[j] = 临时;
                }
            }
        }
    }
}
​
// 使用不同的比较方法
排序工具 工具 = new 排序工具();
int[] 数字 = { 3, 1, 4, 1, 5, 9, 2 };
​
// 升序排序
工具.排序(数字, (a, b) => a > b);
Console.WriteLine("升序: " + string.Join(", ", 数字));
​
// 降序排序  
工具.排序(数字, (a, b) => a < b);
Console.WriteLine("降序: " + string.Join(", ", 数字));

七、委托与事件的关系

事件是基于委托的

public class 温度监测器
{
    // 1. 定义委托
    public delegate void 温度报警委托(double 温度);
    
    // 2. 基于委托定义事件
    public event 温度报警委托 温度过高;
    
    private double _当前温度;
    
    public double 当前温度
    {
        get { return _当前温度; }
        set
        {
            _当前温度 = value;
            if (_当前温度 > 30)
            {
                // 3. 触发事件
                温度过高?.Invoke(_当前温度);
            }
        }
    }
}

public class 报警系统
{
    public void 发出警报(double 温度)
    {
        Console.WriteLine($"警报!温度过高:{温度}度");
    }
}

// 使用
温度监测器 监测器 = new 温度监测器();
报警系统 报警器 = new 报警系统();

// 注册事件处理
监测器.温度过高 += 报警器.发出警报;

// 当温度变化时会自动触发事件
监测器.当前温度 = 35;  // 输出:警报!温度过高:35度

八、初学者实践建议

第一步:理解委托的概念

记住:委托就是"方法的容器"

第二步:从简单委托开始

// 1. 定义委托
delegate void 简单任务();

// 2. 创建方法
void 问好() { Console.WriteLine("你好!"); }
void 再见() { Console.WriteLine("再见!"); }

// 3. 使用委托
简单任务 任务 = 问好;
任务();  // 输出:你好!

任务 = 再见;  
任务();  // 输出:再见!

第三步:尝试带参数的委托

delegate string 格式化委托(string 输入);

string 转大写(string 文本) { return 文本.ToUpper(); }
string 转小写(string 文本) { return 文本.ToLower(); }

格式化委托 格式化器 = 转大写;
Console.WriteLine(格式化器("Hello"));  // 输出:HELLO

格式化器 = 转小写;
Console.WriteLine(格式化器("HELLO"));  // 输出:hello

第四步:使用多播委托

delegate void 日志委托(string 消息);

void 控制台日志(string 消息) { Console.WriteLine("控制台: " + 消息); }
void 文件日志(string 消息) { Console.WriteLine("文件: " + 消息); }

日志委托 日志系统 = 控制台日志;
日志系统 += 文件日志;  // 添加第二个方法

日志系统("系统启动");  
// 输出:
// 控制台: 系统启动
// 文件: 系统启动

九、记住这个简单比喻

委托就像"多功能遥控器":

  • 你可以设置遥控器的按钮1 = 电视开机

  • 也可以设置按钮1 = 空调开机

  • 还可以设置一个按钮同时控制电视+空调+灯光

同一个按钮,不同时间可以执行不同功能!

委托让方法调用变得灵活、动态、可扩展,是C#事件和回调机制的基础!


评论