.NET进阶——深入理解委托(1)委托入门

一、什么是委托

委托就相当于是一个可以存放方法的箱子,我们可以通过这个"箱子"调用里面的方法,比如我下面的代码:

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 0.背景:委托定义与方法定义 // 创建了一个名为MyDelegate的委托,这个委托里面装的函数必须有两个int类型的参数,一个int类型的返回值! public delegate int MyDelegate(int a,int b); // 创建了一个Add方法,需要两个int参数,一个int返回值,与上面委托要求的相符! int Add(int a, int b) { return a + b; } // 1.实例化类, MyClass myClass = new MyClass(); // 2.实例化委托并且将方法装载到委托中 MyDelegate myDelegate1 = new MyDelegate(myClass.Add); // 3.调用委托,即调用委托这个"箱子里的方法Add(1,2)" int result = myDelegate1.Invoke(1,2);

这里我演示了如何简单的使用委托,那么有的同学看到可能会疑惑:

为什么要使用委托?直接调用Add(1,2)函数不就好了吗?为什么要写这么麻烦?

这个问题困扰着每一个委托初学者,其实这个问题非常好,但是现在我们先往下学,在本文,我会告诉大家这个问题的答案。

1.1 创建委托的语法

对我来说,我知道创建委托的方法有以下几个

① 显示创建委托

这是 C# 1.0 引入的最基础方式,通过 new 关键字显式创建委托实例,并将目标方法作为构造函数参数。

复制代码

cs

体验AI代码助手

代码解读

复制代码

委托类型 委托实例 = new 委托类型(目标方法);

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 1. 定义委托类型 public delegate void MyDelegate(string message); // 2. 目标方法(静态或实例方法均可) public static void ShowMessage(string msg) { Console.WriteLine(msg); } // 3. 显式实例化委托 MyDelegate del = new MyDelegate(ShowMessage); del("Hello, 显式实例化委托!"); // 调用委托

② 方法组转换(简化方式)

C# 2.0 引入了方法组转换 ,允许直接将方法名赋值给委托实例(编译器自动完成 new 操作)。这是最常用的方式之一。

复制代码

cs

体验AI代码助手

代码解读

复制代码

委托类型 委托实例 = 目标方法;

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 复用上面的 MyDelegate 委托和 ShowMessage 方法 MyDelegate del = ShowMessage; // 方法组转换 del("Hello, 方法组转换!");

说明

  • 目标方法可以是静态方法 (直接用类名调用)或实例方法(需先创建类实例)。
  • 方法签名(参数类型、返回值类型)必须与委托完全匹配。
③ 匿名方法(临时方法)

C# 2.0 引入了匿名方法,允许在创建委托时直接定义方法体,无需单独声明命名方法。适合临时使用的简单逻辑。

语法

复制代码

cs

体验AI代码助手

代码解读

复制代码

委托类型 委托实例 = delegate(参数列表) { // 方法体 };

示例

复制代码

cs

体验AI代码助手

代码解读

复制代码

public delegate void MyDelegate(string message); MyDelegate del = delegate(string msg) { Console.WriteLine("匿名方法:" + msg); }; del("Hello, 匿名方法!");

说明

  • 匿名方法可以访问外部变量(闭包),但需注意变量生命周期。
  • 若委托无参数,delegate() 中的参数列表可省略(仅匿名方法支持)。
④ Lambda 表达式(最简洁方式

C# 3.0 引入了Lambda 表达式,是匿名方法的更简洁语法,广泛用于 LINQ、事件处理等场景。

语法

  • 无参数:() => { 方法体 }() => 表达式(单行可省略大括号)
  • 有参数:(参数1, 参数2) => { 方法体 }参数 => 表达式(单参数可省略括号)

示例

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 1. 无参数委托 public delegate void NoParamDelegate(); NoParamDelegate del1 = () => Console.WriteLine("无参数 Lambda!"); del1(); // 2. 有参数委托(单行简化) MyDelegate del2 = msg => Console.WriteLine("Lambda:" + msg); del2("Hello, Lambda!"); // 3. 多行 Lambda(需大括号和 return) public delegate int CalcDelegate(int a, int b); CalcDelegate del3 = (a, b) => { int sum = a + b; return sum; }; int result = del3(10, 20); // 结果:30

说明

  • Lambda 表达式同样支持闭包,访问外部变量。
  • 编译器会根据委托签名自动推断参数类型(若委托是泛型委托,如 Func/Action,则参数类型需显式或由上下文推断)。

1.2 调用委托的语法

① 基础调用:Invoke()方法

Invoke() 是委托类型的实例方法,用于显式调用委托所引用的方法。这是最明确的调用方式,适合强调 "通过委托调用" 的场景。

复制代码

ini

体验AI代码助手

代码解读

复制代码

委托实例.Invoke(参数列表);

复制代码

cs

体验AI代码助手

代码解读

复制代码

public delegate void MyDelegate(string message); public static void ShowMessage(string msg) { Console.WriteLine(msg); } // 创建委托实例 MyDelegate del = ShowMessage; // 使用 Invoke() 调用 del.Invoke("Hello via Invoke!"); // 输出:Hello via Invoke!

2. 简化调用:直接调用(编译器语法糖)

C# 允许将委托实例直接作为方法名 调用,编译器会自动将其转换为 Invoke() 调用。这是最常用的简化语法。

语法:
复制代码

cs

体验AI代码助手

代码解读

复制代码

委托实例(参数列表);

示例:
复制代码

cs

体验AI代码助手

代码解读

复制代码

// 复用上面的委托和方法 MyDelegate del = ShowMessage; // 直接调用(等价于 del.Invoke(...)) del("Hello via direct call!"); // 输出:Hello via direct call!

1.2 委托的原理

谈到这里,我们就可以好好聊聊之前的问题了,委托到底是什么?

① 先想清楚:为什么需要委托?

假设你是一个房东 ,想把房子租出去,但你不想自己找租客、签合同、收房租(太麻烦了)。这时候你会找一个房产中介 ,把 "租房" 这件事委托给中介去做。

在编程里,方法 就像 "租房" 这件事,而委托 就是 "房产中介"------ 它帮你管理方法的调用,让你不用直接和方法打交道。

② 委托的本质:方法的 "代言人"

委托的核心作用是:把 "方法" 变成一个可以 "传递、存储、调用" 的 "对象"

用更通俗的话讲:

  • 委托是一个 "方法的代言人":它知道 "要调用哪个方法""怎么调用这个方法"。
  • 委托是一个 "类型安全的中介":它只接受 "符合要求的方法"(比如参数、返回值要匹配,就像中介只接受 "合法的房东")。
  • 委托是一个 "可以同时干多件事的中介":它可以同时帮你调用多个方法(比如中介同时帮你找租客、修房子、收房租)。
③ 用 "租房" 例子拆解委托的核心功能

我们把 "租房" 的流程对应到委托的使用上,你就能完全理解了:

第一步:定义 "委托类型"(相当于 "中介的服务范围")

房东找中介前,得先明确 "中介要做什么"(比如 "帮我找租客,签合同,收房租")。在编程里,这就是定义委托类型,规定 "这个委托能代言什么样的方法"(比如参数类型、返回值类型)。

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 定义一个委托类型:"租房中介",负责"处理租房相关的事" // (参数是租客名字,返回值是租金) public delegate int RentHouseDelegate(string tenantName);

第二步:准备 "要委托的方法"(相当于 "房东的具体需求")

房东需要明确 "具体要做哪些事",比如:

  • 找租客:FindTenant 方法
  • 签合同:SignContract 方法
  • 收房租:CollectRent 方法
复制代码

cs

体验AI代码助手

代码解读

复制代码

// 房东的具体方法(要委托给中介的事) public class Landlord { // 找租客:返回押金(比如1000元) public int FindTenant(string tenant) { Console.WriteLine($"帮房东找租客:{tenant}"); return 1000; // 押金 } // 签合同:返回第一个月租金(比如3000元) public int SignContract(string tenant) { Console.WriteLine($"帮房东和租客 {tenant} 签合同"); return 3000; // 首月租金 } }

第三步:创建 "委托实例"(相当于 "雇佣中介")

房东找到中介,把 "找租客""签合同" 这些事委托给中介(绑定方法到委托实例)。

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 创建房东实例(具体的房东) Landlord landlord = new Landlord(); // 雇佣中介:把"找租客"方法委托给中介 RentHouseDelegate agent = landlord.FindTenant; // 还可以让中介同时干多件事:再委托"签合同"方法 agent += landlord.SignContract;

4. 第四步:调用 "委托"(相当于 "让中介干活")

现在,房东只需要告诉中介 "租客名字" ,中介就会自动执行所有委托的方法(找租客 → 签合同)。

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 让中介干活:处理租客"张三"的租房事宜 int totalMoney = agent("张三"); Console.WriteLine($"总共收到钱:{totalMoney}元");

5. 执行结果(中介干的活)
复制代码

arduino

体验AI代码助手

代码解读

复制代码

帮房东找租客:张三 帮房东和租客 张三 签合同 总共收到钱:3000元 // 注意:多播委托只返回最后一个方法的结果(签合同的3000元)

④ 委托的本质总结(一句话)

委托就是一个 "方法的代言人",它帮你管理方法的调用,让方法可以像变量一样 "传递、存储、批量调用"

⑤ 为什么不用直接调用方法?

你可能会问:"我直接调用 landlord.FindTenant("张三") 不行吗?为什么要找中介?"

这就涉及到委托的核心价值:解决 "方法不能直接传递" 的问题

比如:

  • 你写了一个 "按钮" 控件,点击按钮时要执行 "用户自定义的方法",但你不知道用户会写什么方法(可能是 "打开文件",也可能是 "保存数据")。这时候,你就需要一个委托来 "接收" 用户的方法,点击时再调用。
  • 你写了一个 "排序" 方法,需要用户提供 "比较规则"(比如按年龄排序还是按姓名排序),但你不知道用户的比较规则是什么。这时候,你就需要一个委托来 "接收" 用户的比较方法,排序时再调用。
⑥ 再举一个更简单的例子:按钮点击事件

这是委托最常见的应用场景,你肯定见过:

复制代码

cs

体验AI代码助手

代码解读

复制代码

// 1. 系统定义了一个委托类型:"点击事件的代言人" public delegate void EventHandler(object sender, EventArgs e); // 2. 按钮有一个"点击事件"(本质是委托实例) button.Click += new EventHandler(OnButtonClick); // 3. 你写的"点击后要执行的方法" private void OnButtonClick(object sender, EventArgs e) { Console.WriteLine("按钮被点击了!"); }

这里的 EventHandler 就是委托,它帮系统 "记住" 了你要执行的 OnButtonClick 方法,当按钮被点击时,系统就调用这个委托,从而执行你的方法。

核心结论

委托的本质其实很简单:它是一个 "方法的代言人",让方法可以像变量一样被传递、存储和调用

就像你找中介租房,不用自己跑断腿;编程里用委托,不用直接调用复杂的方法,而是让委托帮你搞定。

二、系统内置的两个委托

在 .NET 框架中,ActionFunc 是最常用的两个内置泛型委托 ,它们由 System 命名空间提供,无需手动定义,可直接用于几乎所有委托场景。这两个委托的设计目标是减少自定义委托的数量,提高代码复用性和可读性。

Action 委托:无返回值的方法引用

Action 委托用于封装无返回值 的方法(即 void 返回类型)。它支持 0 到 16 个输入参数,通过泛型参数指定参数类型。

1. 基本定义
  • 无参数Action用于封装 "无参数、无返回值" 的方法。
  • 带参数Action<T1>Action<T1, T2>、...、Action<T1, T2, ..., T16>用于封装 "带 1~16 个参数、无返回值" 的方法,泛型参数 T1~T16 表示参数类型。
2. 示例:使用 Action 委托
复制代码

cs

体验AI代码助手

代码解读

复制代码

using System; class Program { static void Main() { // 1. 无参数 Action:封装无参数方法 Action printHello = () => Console.WriteLine("Hello, Action!"); printHello(); // 输出:Hello, Action! // 2. 带 1 个参数 Action:封装带 1 个参数的方法 Action<string> printMessage = msg => Console.WriteLine($"Message: {msg}"); printMessage("你好,世界!"); // 输出:Message: 你好,世界! // 3. 带 2 个参数 Action:封装带 2 个参数的方法 Action<string, int> printInfo = (name, age) => Console.WriteLine($"{name} 今年 {age} 岁"); printInfo("张三", 25); // 输出:张三 今年 25 岁 } }

Func 委托:有返回值的方法引用

Func 委托用于封装有返回值 的方法。它支持 0 到 16 个输入参数,最后一个泛型参数始终表示返回值类型

1. 基本定义
  • 无输入参数,仅返回值Func<TResult>用于封装 "无参数、返回值类型为 TResult" 的方法。
  • 带输入参数和返回值Func<T1, TResult>Func<T1, T2, TResult>、...、Func<T1, T2, ..., T16, TResult>用于封装 "带 1~16 个输入参数、返回值类型为 TResult" 的方法,前 N 个泛型参数表示输入参数类型,最后一个表示返回值类型。
2. 示例:使用 Func 委托
复制代码

cs

体验AI代码助手

代码解读

复制代码

using System; class Program { static void Main() { // 1. 无输入参数,返回 int:封装无参数、返回 int 的方法 Func<int> getRandomNumber = () => new Random().Next(1, 100); int randomNum = getRandomNumber(); Console.WriteLine($"随机数:{randomNum}"); // 输出:随机数:[1-99的随机数] // 2. 带 1 个输入参数,返回 string:封装带 1 个参数、返回 string 的方法 Func<int, string> numberToString = num => $"数字是:{num}"; string result1 = numberToString(123); Console.WriteLine(result1); // 输出:数字是:123 // 3. 带 2 个输入参数,返回 int:封装带 2 个参数、返回 int 的方法 Func<int, int, int> add = (a, b) => a + b; int sum = add(10, 20); Console.WriteLine($"和:{sum}"); // 输出:和:30 } }

三、ActionFunc 的核心区别

特性 Action 委托 Func 委托
返回值 无返回值(void 必须有返回值(最后一个泛型参数为返回值类型)
泛型参数 仅表示输入参数(0~16 个) 前 N 个为输入参数,最后一个为返回值类型(0~16 个输入参数)
适用场景 用于 "执行操作"(如打印、修改状态) 用于 "计算结果"(如求和、转换类型)

补充:Predicate 委托(特殊的 Func

除了 ActionFunc,.NET 还提供了 Predicate<T> 委托,它是 Func<T, bool> 的 "语法糖",专门用于返回布尔值的条件判断(如 "判断元素是否满足条件")。

基本定义
  • Predicate<T>:封装 "输入参数为 T、返回值为 bool" 的方法,等价于 Func<T, bool>
示例:使用 Predicate 委托
复制代码

cs

体验AI代码助手

代码解读

复制代码

using System; class Program { static void Main() { // 检查数字是否为偶数(等价于 Func<int, bool>) Predicate<int> isEven = num => num % 2 == 0; Console.WriteLine(isEven(4)); // 输出:True Console.WriteLine(isEven(5)); // 输出:False } }

学完了这些,我们基本上算委托入门了。

相关推荐
运维@小兵1 天前
使用Spring-AI的chatMemoryAdvisor实现多轮会话
java·人工智能·spring
小马爱打代码1 天前
Spring AI:Apache Tika 读取 Word、PPT 文档
人工智能·spring·apache
武藤一雄1 天前
.NET中到底什么是SignalR (持续更新)
后端·微软·c#·asp.net·.net·.netcore·signalr
奔跑中的小象1 天前
统信UOS V2500服务器操作系统+海光K100 AI卡环境下VLLM服务部署
服务器·人工智能·uos·vllm·统信·海光k100
秋邱1 天前
AR 商业化闭环:从技术交付到生态变现
后端·ar·restful
云飞云共享云桌面1 天前
告别传统电脑——智能装备工厂采用共享云桌面方案实现降本增效
运维·服务器·网络·人工智能·电脑
galaxylove1 天前
Gartner预测2026年:不断演变的威胁和人工智能的应用将改变基础设施安全
人工智能·安全
我很哇塞耶1 天前
KDD 2026|小红书搜索:生成式相关性让搜索“会思考”
人工智能·ai·大模型
啊巴矲1 天前
小白从零开始勇闯人工智能:机器学习初级篇(pandas库)
人工智能·机器学习·pandas