跳过正文

C#:委托和异步回调

·3343 字·7 分钟
杂谈 技术
AxonSin
作者
AxonSin
梦想是复活在赛博世界,成为一名赛博垃圾人。
目录

委托和回调
#

🎯 什么是委托?—— 用“外卖订单”来比喻
#

想象一下,你开了一家外卖店,顾客下单后,你需要通知不同的“执行人”去完成任务:

  • 有人负责做菜(厨师)
  • 有人负责打包(打包员)
  • 有人负责送外卖(骑手)

但你不想每次都硬编码(写死)让“张三”做菜、“李四”打包。你希望更灵活:谁有空,就让谁干!

这就需要一个“通知机制”——你不知道具体是谁干活,但你知道“该叫人来做这件事了”。

在 C# 中,委托就是这个“通知机制”


🧱 第一步:定义“任务类型”—— 委托的声明
#

你先要定义一个“岗位职责说明书”,比如“做饭的人必须会做菜”。

// 委托声明:定义一种“能做饭”的岗位
public delegate void CookDelegate(string dishName);

👉 解释:

  • delegate:关键词,表示这是一个委托。
  • void:表示这个岗位完成任务后不返回结果(比如不需要返回“菜好不好吃”)。
  • CookDelegate:这是这个岗位的名字。
  • string dishName:表示这个岗位需要一个参数,比如“宫保鸡丁”。

✅ 这就像你说:“所有应聘‘厨师’岗位的人,必须会接收一个菜名,并能做出来。”


🧍 第二步:创建“应聘者”—— 具体的方法
#

现在,有两位厨师来应聘:

public class Chef
{
    public static void ZhangSanCook(string dish)
    {
        Console.WriteLine($"张三正在做:{dish}");
    }

    public static void LiSiCook(string dish)
    {
        Console.WriteLine($"李四正在做:{dish}");
    }
}

👉 这两个方法都符合 CookDelegate 的要求:接受一个字符串,返回 void。


📞 第三步:发布“招聘通知”—— 创建委托实例
#

现在你开始招人:

// 创建一个委托变量,表示“当前负责做饭的人”
CookDelegate cookJob = null;

// 张三来应聘,你把他安排上
cookJob = Chef.ZhangSanCook;

// 或者你也可以换李四
// cookJob = Chef.LiSiCook;

👉 这就像你说:“现在让张三来做饭。”


🔔 第四步:下达任务 —— 调用委托
#

当顾客下单“宫保鸡丁”时,你不需要关心是谁在做,你只管“通知做饭的人”:

if (cookJob != null)
{
    cookJob("宫保鸡丁"); // 调用委托
}

输出:

张三正在做:宫保鸡丁

✅ 你没有写死“张三来做”,而是通过委托“间接调用”了方法。


🚀 更强大的功能:多个人一起干!—— 多播委托(Multicast Delegate)
#

委托还能“通知多人”!比如,做菜的同时,还要通知打包员和骑手。

我们再定义两个委托:

public delegate void PackDelegate(string dish);
public delegate void DeliverDelegate(string address);

然后创建方法:

public class Staff
{
    public static void PackFood(string dish)
    {
        Console.WriteLine($"打包员正在打包:{dish}");
    }

    public static void DeliverFood(string address)
    {
        Console.WriteLine($"骑手正在送往:{address}");
    }
}

现在,你可以把多个任务“串”起来,像流水线一样:

// 使用内置的 Action 委托(更简单)
Action<string> workflow = null;

workflow += Chef.ZhangSanCook;     // 第一步:做饭
workflow += Staff.PackFood;        // 第二步:打包
workflow += (address) => Staff.DeliverFood("北京市朝阳区"); // 第三步:送外卖

// 一键触发整个流程!
workflow?.Invoke("宫保鸡丁");

输出:

张三正在做:宫保鸡丁
打包员正在打包:宫保鸡丁
骑手正在送往:北京市朝阳区

👉 += 表示“再加一个人来干活”,这就是多播委托,像一条流水线,一个任务接一个任务执行。


🌟 委托的真正用途:解耦 + 回调
#

1. 解耦(Decoupling)
#

你写一个“下单系统”,但你不需要知道谁来做饭、谁来送外卖。你只关心:“有订单时,调用这个委托就行。”
这样,厨师换了、骑手换了,你的主逻辑完全不用改!

2. 回调(Callback)
#

比如你调用一个“异步做饭”方法,做完后要通知你:

public void StartCookingAsync(string dish, Action onComplete)
{
    // 模拟做饭耗时
    Task.Delay(2000).Wait();
    Console.WriteLine($"{dish} 做好了!");
    
    // 做完后,回调通知你
    onComplete?.Invoke();
}

// 使用时:
StartCookingAsync("红烧肉", () => {
    Console.WriteLine("可以开饭啦!");
});

👉 这就是“你先去做饭,做完了告诉我一声”。


✅ 总结:委托到底是什么?
#

比喻 编程概念
岗位招聘说明书 delegate void CookDelegate(string dish);
应聘的厨师 ZhangSanCook, LiSiCook 方法
安排谁上岗 cookJob = ZhangSanCook;
下达任务 cookJob("宫保鸡丁");
流水线作业 多播委托 +=
做完通知我 回调(Callback)

🎯 一句话总结

委托就是“把方法当作变量来传递”,让你的代码更灵活、可扩展、可维护。

就像你不需要知道谁在干活,你只负责“按按钮”,系统就会自动让正确的人完成任务。

invoke和C#中的lambda表达式
#

🧩 代码回顾:
#

onComplete?.Invoke();

StartCookingAsync("红烧肉", () => {
    Console.WriteLine("可以开饭啦!");
});

我们要理解的是:这两个部分是如何配合工作的?为什么这样写?


🎯 场景设定:做饭需要时间
#

想象一下,你让厨师去做一道“红烧肉”,但做饭要花 2 秒钟。你不想傻等,而是说:

“你去做好了红烧肉之后,记得告诉我一声,我好去吃饭。”

这个“告诉我一声”,就是所谓的 回调(Callback),而 Action onComplete 就是“告诉我”的那个方式。


✅ 第一部分:onComplete?.Invoke();
#

我们先看定义这个方法的地方:

public void StartCookingAsync(string dish, Action onComplete)
{
    // 模拟做饭耗时
    Task.Delay(2000).Wait();  // 等2秒,假装在做饭
    Console.WriteLine($"{dish} 做好了!");

    // 做完后,回调通知你
    onComplete?.Invoke();
}

🔍 逐行解释:
#

  1. Action onComplete
    • 这是一个委托参数,意思是:“请告诉我,做完饭后要执行哪个方法?”
    • Action 是 C# 内置的一个委托类型,表示“一个没有参数、没有返回值的方法”。
  2. onComplete?.Invoke();
    • 这是关键!我们来拆开看:
部分 含义
onComplete 就是你传进来的“完成后要执行的方法”
?. 空值条件运算符:意思是“如果 onComplete 不是 null,才继续调用”
Invoke() 调用这个委托指向的方法(也就是执行它)

✅ 所以这行代码的意思是:

“如果有人告诉我‘做完要做什么’,那我现在就执行那个操作;如果没人告诉我,那就算了。”

👉 防止空指针错误!如果不加 ?,而你又没传回调,程序就会报错:“你试图调用一个 null 的方法”。


✅ 第二部分:() => { ... } 是什么?
#

再看调用的地方:

StartCookingAsync("红烧肉", () => {
    Console.WriteLine("可以开饭啦!");
});

🔍 拆解这个调用:
#

  • "红烧肉":这是第一个参数,表示要做哪道菜。
  • () => { ... }:这是第二个参数,就是那个 Action onComplete

() => { ... } 到底是什么?
#

这是 C# 的 Lambda 表达式,你可以把它理解为:

“一个匿名的方法(没有名字的方法),它没有参数,执行的时候就打印一句话。”

等价于:

void OnCookingFinished()
{
    Console.WriteLine("可以开饭啦!");
}

但我们不想专门去定义一个方法,所以用 () => { } 来“现场写一个临时方法”。

写法 含义
() 这个方法不需要任何参数
=> “指向”要执行的代码,读作“goes to”
{ ... } 要执行的具体操作

所以:

() => {
    Console.WriteLine("可以开饭啦!");
}

意思就是:

“当做饭做完时,请执行:打印‘可以开饭啦!’”


🔄 两个部分如何配合?—— 完整流程
#

我们把整个流程串起来:

// 你调用这个方法
StartCookingAsync("红烧肉", () => {
    Console.WriteLine("可以开饭啦!");
});
  1. 程序开始做饭,等 2 秒。
  2. 打印:红烧肉 做好了!
  3. 然后执行:onComplete?.Invoke();
    • 此时 onComplete 指向的是你传进来的那个 () => { ... }
    • 所以就会执行里面的代码:Console.WriteLine("可以开饭啦!");

🎯 最终输出:

红烧肉 做好了!
可以开饭啦!

🧠 类比理解:打电话预约
#

想象你打电话给餐厅:

你:“帮我做份红烧肉,做好了打电话通知我。”

  • “打电话通知我” → 就是 Action onComplete
  • 你留的电话号码 → 就是 () => { Console.WriteLine("可以开饭啦!"); }
  • 餐厅做完菜后打电话 → 就是 onComplete?.Invoke();

如果没留电话(onComplete 是 null),那餐厅当然没法通知你,所以加个 ?. 防止出错。


✅ 总结:一句话讲清楚
#

  • onComplete?.Invoke();
    “如果有人留了‘事后要做的事’,现在就去做那件事。”
  • () => { ... }
    “这是我留的‘事后要做的事’——打印一句话。”

👉 它们配合起来,就实现了“异步完成后的通知机制”,这就是委托最常用的场景之一。

Reply by Email

相关文章

Huggingface镜像站
·396 字·1 分钟
杂谈 技术
技术分享
Unity命名规范检查器
·2131 字·5 分钟
杂谈 Unity 材质 色彩 故障排除
问题解决方案
EXR vs. PNG
·1503 字·3 分钟
杂谈 Unity Animation 算法 配置
算法原理解析