当前位置: 首页 > news >正文

C#学习笔记——委托

知识全景思维导图

一、 委托到底是什么?

通俗理解是把方法(函数)当作包裹交给这个代理人,当你喊“执行”的时候,代理人就会去调用这个方法。

委托的“三步走”战略

1. 声明(定义契约)
// 定义一个契约:我只能代理 [返回值为int,接收两个int参数] 的方法 public delegate int MathOperation(int x, int y);
2. 实例化(绑定方法)
// 传统写法 MathOperation op1 = new MathOperation(Add); // C# 2.0 简化写法 (语法糖) MathOperation op2 = Add; // 绑定 Lambda 表达式 MathOperation op3 = (x, y) => x * y;
3. 调用(触发执行)
int result1 = op1(3, 4); // 直接像方法一样调用 (常用) int result2 = op1.Invoke(3, 4); // 使用 Invoke 方法调用

注意:委托在编译时会严格校验方法签名,确保类型匹配和调用安全

public delegate void ProcessData(string input); // ✅ 合法绑定 ProcessData handler = s => Console.WriteLine(s); // ❌ 非法绑定 参数类型不匹配 (编译错误) // ProcessData errorHandler = (int i) => Console.WriteLine(i); // ❌ 非法绑定 参数个数不匹配 (编译错误) // ProcessData errorHandler1 = (i,j) => Console.WriteLine(i,j); // ❌ 非法绑定 返回值类型不匹配 (编译错误) // ProcessData errorHandler2 = s => s;

二、 为什么需要委托?

假设我们在开发一个游戏主界面,有 6 个按钮(开始游戏、排行榜、分享好友等)。
如果我们为每个按钮写一个单独的 StartButton、FriendButton 类,代码会极度冗余。

委托的解决方案:只写一个 Button 类,将“点击后干什么”交给外界决定!

例如

class Button { public delegate void OnClickDelegate(); // 1. 定义委托类型 public OnClickDelegate? onClick = null; // 2. 声明委托变量 public void Click() { Console.WriteLine("按钮被按下了..."); onClick?.Invoke(); // 3. 如果有人绑定了方法,就执行它! } } // 外界使用: Button startBtn = new Button(); startBtn.Click();

三、多播委托

多播委托允许一个委托变量内部维护一个方法链表。当你调用这个委托时,它会按照顺序把链表里的方法全执行一遍。

  • +=:将方法加入链表。

  • -=:将方法移出链表。

金典案例:小明让小张帮忙买水

(1) 先定义一个买东西的类

public class Zhang { public delegate void BuySomethingDelegate(); public void Buywater() { Console.WriteLine("买水!"); } public void BuyKFC() { Console.WriteLine("买肯德基"); } public void BuyHotDog() { Console.WriteLine("买热狗"); } }

(2) 小张帮小明完成了买水的操作

Zhang z = new Zhang(); BuySomethingDelegate bsd = new BuySomethingDelegate(z.Buywater); bsd(); //bsd.Invoke();

(3) 小明突然想吃东西,又让小张顺路带个热狗和肯德基

Zhang z = new Zhang(); BuySomethingDelegate bsd = new BuySomethingDelegate(z.Buywater); bsd += z.BuyHotDog; bsd += z.BuyKFC; bsd();

(4)还没付钱,小明怕吃不下又让小明把热狗退了

Zhang z = new Zhang(); BuySomethingDelegate bsd = new BuySomethingDelegate(z.Buywater); bsd += z.BuyHotDog; bsd += z.BuyKFC; bsd -= z.BuyHotDog; bsd();

多播委托的特点

1. 调用顺序:方法按照它们被添加的顺序调用
2. 返回值:如果委托有返回值,只有最后一个方法的返回值会被保留
3. 异常处理:如果某个方法抛出异常,后续方法不会被调用

四、 微软的三大委托

为了避免我们每次都要 public delegate void XXX(),微软在 .NET 中直接帮我们提前定义好了三种最常用的泛型委托。

委托名称特点适用场景签名示例
Action<T...>无返回值(最多支持16个参数)只需要执行动作,不需要汇报结果Action<string> print = s => Console.WriteLine(s);
Func<T..., TResult>有返回值(最后一个泛型是返回值类型)需要计算并返回结果Func<int, int, int> add = (a, b) => a + b;
Predicate<T>仅1个参数,返回 bool条件判断、集合筛选 (Find, FindAll)Predicate<int> isEven = n => n % 2 == 0;

五、 事件 (Event)

1. 什么是事件?

事件本质上是一种特殊的多播委托。 它是基于“发布-订阅(Pub/Sub)”模型的。
发布者 (Publisher):拥有事件的类,决定何时触发事件。
订阅者 (Subscriber):监听事件的类,决定发生事件后做什么。

案例:

// 发布者类 public class PublisherClass { // 和事件搭配的委托 public delegate void PubDelegate(); // 定义事件 public event PubDelegate? PubEvent; // 编写处理事件的具体逻辑 public void EventHandling() { if (PubEvent == null) { Console.WriteLine("需要注册事件的啊"); } else { // 执行注册的事件 PubEvent(); } } } // 订阅者类 public class SubscriberClass { public void PrintOut() { Console.WriteLine("执行了订阅者类中的事件。"); Console.ReadLine(); } } internal class Class1 { static void Main(string[] args) { // 实例化对象 PublisherClass p = new PublisherClass(); SubscriberClass s = new SubscriberClass(); // 执行事件 p.EventHandling(); // 注册事件 p.PubEvent += new PublisherClass.PubDelegate(s.PrintOut); //可以简写: //p.PubEvent += s.PrintOut; // 执行事件 p.EventHandling(); } }

2. 使用EventHandler 声明事件

EventHandler 是 .NET 中最基本的事件委托,其定义如下:

public delegate void EventHandler(object sender, EventArgs e); //sender:触发事件的对象(通常是 this)。 //e:事件参数,如果不需要传递额外数据,可以使用 EventArgs.Empty。

案例:

//事件发布者 public class Button { // 1. 定义事件 public event EventHandler Click; // 2. 触发事件的方法(遵循 .NET 规范,通常命名为 OnXXX)编写处理事件的具体逻辑 public virtual void OnClick(EventArgs e) { Click?.Invoke(this, e); // 安全调用,避免 NullReferenceException } }

EventHandler类型的事件可以通过+=运算符来订阅事件处理程序.有下面这几种写法

(1) 使用 Lambda 表达式(推荐简洁写法)

适用场景:简单逻辑,无需复用的事件处理。

btn.Click += (sender, e) => { Console.WriteLine($"事件触发者:{sender}"); //事件触发者 Console.WriteLine($"事件参数:{e}"); Console.WriteLine("Click event handled!"); };

(2) 使用方法(符合传统 .NET 风格)

适用场景:需要复用的逻辑或复杂处理。

btn.Click += OnButtonClick; // 事件处理方法 private static void OnButtonClick(object sender, EventArgs e) { Console.WriteLine($"事件触发者: {sender}, 参数: {e}"); }

六、 事件 vs 委托

对比维度委托 (Delegate)事件 (Event)
本质是一种类型(Type),像类一样是委托变量的一个包装器(类似于属性包装了字段)
访问控制外部可以随意赋值 (=) 或清空外部只能订阅 (+=) 或退订 (-=)
触发权限任何人拿到委托变量都可以 Invoke()只有声明事件的类自己才能触发 (Invoke)
接口定义不能直接定义在接口中可以定义在接口中 (event EventHandler XXX;)
主要用途灵活的方法传递、回调函数、LINQ查询严格的发布-订阅模式,状态通知(如 UI 交互)

总结:委托是底层基础,事件是基于委托做的高级封装,为的是代码的安全解耦

http://www.jsqmd.com/news/487008/

相关文章:

  • Ai8051 独立按键控制LED实验
  • 福宝的「熵减日记」:从「记忆混乱」到「响应如飞」的72小时进化史 [特殊字符][特殊字符]
  • Thinkphp和Laravel框架都支持基于微信小程序的公开课选课打卡管理系统的设计与实现-
  • 2026年企业健身房规划方案,打造健康办公新生态
  • AC 双链路备份与冷热备核心知识点总结
  • qt PlotJuggler
  • 对量化交易未来的思考
  • 老品牌为什么在 AI 推荐里比较里靠后:一次公开表达收口排查
  • 小程序制作平台有哪些?SaaS模板类平台评测
  • 测试文章发布 - 编辑版本1773572369633
  • 专注AI优化的服务商
  • 嘎嘎降AI vs 零感AI vs 率零:3款降AI工具深度横评
  • MySQL锁机制:从懵逼到入门,我花了三年
  • Oracle数据库降低水位线
  • RedisSearch 和 Elasticsearch 的 HNSW向量索引对比
  • 计算机毕设云服务器部署避坑指南:从本地到阿里云/腾讯云,一键部署不踩雷
  • If the existence of a group in which one lives is meaningless.
  • 从0开始数据仓库--数据表范式
  • 聚焦民生就医需求 陪诊行业规范提质 北京守嘉陪诊引领行业高质量发展 - 品牌排行榜单
  • 游戏相关AI技术
  • Ozon卖家醒醒吧!别再“手动搬砖”了,你的对手已经在用AI“开挂”了
  • 跨境电商选品师口碑如何?网上教你做电商的可信吗?
  • 世界读书日|与AI共舞,在阅读中寻找你的不可替代性
  • 人工智能之数字生命--“骨架真相”
  • 产品经理工作坊
  • CopyOnWriteArrayList源码学习
  • 拓宽视野:AI领域的非技术岗位全景图(产品、运营、销售等)
  • MySQL 中 MVCC 和锁的关系与配合
  • TR-069 交互流程开发规范
  • 基于SpringBoot与Android的宠物社区APP设计与实现