04-C#.Net-委托和事件-学习笔记

2021-05-24 .NET高级班 04-事件和委托 委托 委托太常见了,能灵活运用可以使你在编程中游刃有余。 简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名) 委托的简单使用 一个委托类型定义了该类型的实例能调用的一类方法,这些方法含有同样的返回类型和同样参数(类型和个数相同)。委托和接口一样,可以定义在类的外部。如下定义了一个委托类型 - Calculator: delegate int Calculator (int x); 此委托适用于任何有着int返回类型和一个int类型参数的方 阅读详情

一、委托(Delegate)基础

1.1 什么是委托?

委托是一种引用类型,本质上是一个类,继承自 MulticastDelegate。它可以封装方法的引用,类似于 C/C++ 中的函数指针,但更加类型安全。

// 委托的定义
public delegate void NoReturnNoPara();
public delegate void NoReturnWithPara(int x, int y);
public delegate int WithReturnNoPara();
public delegate int WithReturnWithPara(out int x, ref int y);

关键点:

  • 委托定义时需要指定参数类型和返回值类型
  • 委托本质是类,会被编译器编译成继承自 MulticastDelegate 的类
  • 委托可以定义在类外部或类内部

1.2 委托的实例化

// 方式1:使用 new 关键字
NoReturnNoPara delegate1 = new NoReturnNoPara(NoReturnNoParaMethod);

// 方式2:直接赋值(语法糖)
NoReturnNoPara delegate2 = NoReturnNoParaMethod;

// 方式3:使用 Lambda 表达式
NoReturnWithPara delegate3 = (x, y) => { Console.WriteLine($"x={x}, y={y}"); };

1.3 委托的执行

// 方式1:使用 Invoke 方法
delegate1.Invoke();

// 方式2:直接调用(语法糖)
delegate1();

// 异步调用(已过时,不推荐)
// delegate1.BeginInvoke(null, null);

1.4 委托的作用和意义

问题场景: 学生问候方式因地域不同而不同(武汉、上海、广东等)

传统方案的问题:

  • 方案1(枚举+switch):业务逻辑耦合,方法不稳定
  • 方案2(多个独立方法):大量重复代码

委托解决方案:

public delegate void SayHiDelegate();

public void SayHiPerfect(SayHiDelegate sayHiDelegate)
{
    Console.WriteLine("问候前,招招手...");
    sayHiDelegate.Invoke();  // 执行传入的具体问候逻辑
}

// 使用
student.SayHiPerfect(student.SayHiWuhan);
student.SayHiPerfect(student.SayHiShanghai);

优势:

  1. 代码稳定:核心方法不需要修改
  2. 逻辑解耦:不同业务逻辑分离
  3. 逻辑重用:去除重复代码

二、框架内置委托

2.1 Action 委托

Action 是无返回值的委托,最多支持 16 个参数。

// 无参数
Action action = () => Console.WriteLine("Hello");

// 有参数
Action<int> action1 = (x) => Console.WriteLine(x);
Action<int, string> action2 = (x, y) => Console.WriteLine($"{x}, {y}");

// 最多16个参数
Action<int, int, int, ..., int> action16 = (p1, p2, ..., p16) => { };

2.2 Func 委托

Func 是有返回值的委托,最多支持 16 个输入参数 + 1 个返回值。

// 无参数,有返回值
Func<int> func = () => 100;

// 有参数,有返回值(最后一个泛型参数是返回值类型)
Func<int, int> func1 = (x) => x * 2;
Func<int, string, int> func2 = (x, y) => x + y.Length;

// 最多16个输入参数 + 1个返回值
Func<int, int, ..., int, string> func17 = (p1, p2, ..., p16) => "result";

为什么要有内置委托?

  • 统一委托类型,避免定义大量自定义委托
  • 减少类型数量(委托本质是类)
  • 提高代码可读性和互操作性

三、多播委托(Multicast Delegate)

3.1 概念

所有委托都继承自 MulticastDelegate,可以通过 += 和 -= 操作符注册和移除多个方法。

Action action = DoNothing;
action += DoNothingStatic;
action += new Student().Study;
action += Student.StudyAdvanced;
action += () => Console.WriteLine("Lambda");

// 执行时按注册顺序依次调用
action.Invoke();

3.2 移除方法

action -= DoNothing;  // 成功移除

// 注意:以下情况无法移除
action -= new Student().Study;  // 失败:不是同一个实例
action -= () => Console.WriteLine("Lambda");  // 失败:Lambda 每次生成不同方法

移除规则:

  • 从后往前逐个匹配
  • 必须是同一个实例的同一个方法
  • 匹配到后移除并停止继续匹配

3.3 异步执行多播委托

// 多播委托不能直接使用 BeginInvoke
// action.BeginInvoke(null, null);  // 错误

// 正确做法:遍历委托列表
foreach (Action item in action.GetInvocationList())
{
    item.BeginInvoke(null, null);  // 每个方法异步执行
}

四、委托的高级应用:嵌套委托(俄罗斯套娃)

4.1 委托嵌套原理

委托可以包装另一个委托,形成多层嵌套,实现 AOP(面向切面编程)。

public delegate void ShowDelegate();

// 核心业务逻辑
ShowDelegate coreMethod = () => Console.WriteLine("核心业务");

// 第一层包装:添加日志
ShowDelegate withLog = () => {
    Console.WriteLine("前置日志");
    coreMethod.Invoke();
    Console.WriteLine("后置日志");
};

// 第二层包装:添加性能监控
ShowDelegate withMonitor = () => {
    Console.WriteLine("开始监控");
    withLog.Invoke();
    Console.WriteLine("结束监控");
};

withMonitor.Invoke();

4.2 结合特性实现自动装配

public abstract class AbstractMethodAttribute : Attribute
{
    public abstract ShowDelegate Do(ShowDelegate action);
}

public class BeforeMethodAttribute : AbstractMethodAttribute
{
    public override ShowDelegate Do(ShowDelegate action)
    {
        return new ShowDelegate(() => {
            Console.WriteLine("前置处理");
            action.Invoke();
            Console.WriteLine("后置处理");
        });
    }
}

// 使用
[BeforeWriteLog]
[BeforeMethod]
public void Method()
{
    Console.WriteLine("核心业务");
}

// 自动装配执行
ShowDelegate showMethod = () => methodInfo.Invoke(instance, null);
foreach (var attribute in methodInfo.GetCustomAttributes().Reverse())
{
    showMethod = attribute.Do(showMethod);
}
showMethod.Invoke();

应用场景:

  • ASP.NET Core 中间件管道
  • 日志记录
  • 性能监控
  • 事务管理
  • 权限验证

五、事件(Event)

5.1 事件的定义

事件是特殊的委托,使用 event 关键字修饰。

public class Cat
{
    // 委托
    public Action MiaoAction = null;
    
    // 事件
    public event Action MiaoEvent = null;
    
    public void Miao()
    {
        Console.WriteLine("喵~");
        MiaoEvent?.Invoke();  // 只能在类内部触发
    }
}

5.2 委托 vs 事件

特性委托事件
本质类特殊的委托
外部赋值可以 = 赋值只能 += 和 -=
外部调用可以直接调用不能外部调用
安全性较低较高
子类访问可以不可以
Cat cat = new Cat();

// 委托可以这样操作
cat.MiaoAction = null;  // 清空所有订阅
cat.MiaoAction();       // 外部直接触发

// 事件只能这样操作
cat.MiaoEvent += Handler;  // 只能添加
cat.MiaoEvent -= Handler;  // 只能移除
// cat.MiaoEvent();        // 错误:不能外部触发
// cat.MiaoEvent = null;   // 错误:不能赋值

5.3 观察者模式(发布-订阅模式)

核心要素:

  1. 发布者(Publisher):定义事件并触发
  2. 订阅者(Subscriber):订阅事件并响应
  3. 订阅关系:通过 += 建立

示例:猫叫引发的连锁反应

public class Cat
{
    public event Action MiaoEvent;
    
    public void Miao()
    {
        Console.WriteLine("猫:喵~");
        MiaoEvent?.Invoke();
    }
}

public class Dog
{
    public void Wang() => Console.WriteLine("狗:汪汪!");
}

public class Mouse
{
    public void Run() => Console.WriteLine("老鼠:快跑!");
}

// 使用
Cat cat = new Cat();
cat.MiaoEvent += new Dog().Wang;
cat.MiaoEvent += new Mouse().Run;
cat.Miao();  // 触发所有订阅者的响应

优势:

  • 职责单一:猫只负责叫,不关心后续反应
  • 降低耦合:猫不依赖狗、老鼠等类
  • 易于扩展:添加新的订阅者不需要修改猫的代码

六、标准事件模式

6.1 标准事件定义

.NET 推荐使用 EventHandler 或 EventHandler<TEventArgs> 定义事件。

// 自定义事件参数
public class PriceChangeEventArgs : EventArgs
{
    public int OldPrice { get; set; }
    public int NewPrice { get; set; }
}

// 发布者
public class Course
{
    private int _price = 5299;
    
    // 标准事件定义
    public event EventHandler<PriceChangeEventArgs> PriceChanged;
    
    public int Price
    {
        get => _price;
        set
        {
            if (value > _price)
            {
                // 触发事件
                PriceChanged?.Invoke(this, new PriceChangeEventArgs
                {
                    OldPrice = _price,
                    NewPrice = value
                });
            }
            _price = value;
        }
    }
}

// 订阅者
public class Student
{
    public void OnPriceChanged(object sender, EventArgs e)
    {
        var course = (Course)sender;
        var args = (PriceChangeEventArgs)e;
        Console.WriteLine($"课程涨价了!从 {args.OldPrice} 涨到 {args.NewPrice}");
        Console.WriteLine("赶紧购买!");
    }
}

// 使用
Course course = new Course();
course.PriceChanged += new Student().OnPriceChanged;
course.Price = 6299;  // 触发事件

6.2 标准事件模式的特点

  1. 事件处理器签名:void Handler(object sender, EventArgs e)

    • sender:事件发布者(谁触发的)
    • e:事件参数(携带什么数据)
  2. 事件参数继承:自定义参数类继承 EventArgs

  3. 命名约定:

    • 事件名:动词或动词短语(如 PriceChanged、Click)
    • 事件参数:XxxEventArgs

七、核心概念总结

7.1 委托的使用场景

  1. 方法内部业务逻辑耦合严重 → 使用委托解耦
  2. 多个方法有大量重复代码 → 使用委托实现逻辑重用
  3. 需要回调机制 → 使用委托传递回调方法
  4. 需要实现策略模式 → 使用委托传递不同策略

7.2 事件的使用场景

  1. 需要通知多个对象某个动作发生 → 使用事件
  2. 需要降低类之间的耦合 → 使用事件实现观察者模式
  3. UI 编程(如 WinForms、WPF)→ 使用事件处理用户交互
  4. 需要保护委托不被外部随意调用 → 使用事件增强安全性

7.3 记忆口诀

  • 委托是盒子:可以装方法,可以传递
  • 多播是链条:一个委托串多个方法
  • 事件是保险:只能订阅,不能乱调
  • 观察者模式:发布订阅,解耦神器

(VIP-朝夕教育)2021-05-24 .NET高级班 04-委托和事件 委托 委托太常见了,能灵活运用可以使你在编程中游刃有余。 简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名) 委托的简单使用 1.定义Student类 /// <summary> /// 学生类 /// </summary> public class Student { public int Id { get; set; } public string Name { get; set; } 阅读详情

相关推荐

海康工业相机使用:常见问题及解决方法

工业相机在使用过程中,经常会因为操作不当、使用环境不当等原因出现一些问题,本篇教程,主要针对海康工业相机常见问题及其定位方法做以简单介绍,希望可以帮助大家。 注:该教程对其他品牌工业相机“常见问题”排查,也具备一定的指导意义。 当出现无法通过客户端发现设备或者无法预览图像等问题,先确认当前的运行环境是否满足以下要求: 确认装有相机驱动软件的电脑网卡为千兆网卡(Gige接口相机); 确认网卡是否已开启巨型帧; 确认电脑与网口相机之间的网络链路为千兆; 确认装有相机驱动软件的USB接口为3...

Cche的博客 3万+

【转载】C# 中的委托和事件(详解:简单易懂的讲解)

本文转载自http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html C# 中的委托和事件(详解) C# 中的委托和事件        委托和事件在 .NET Framework 中的应用非常广泛,然而,较好地理解委托和事件对很多接触 C# 时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的

西刷君的博客 1万+

Kettle下载与安装教程【保姆版】

Kettle简介 Kettle最早是一个开源的ETL工具,全称为KDE Extraction, Transportation, Transformation and Loading Environment。后来Kettle重命名为Pentaho Data Integration 。 它由Java开发,支持跨平台运行,其特性包括:支持100%无编码、拖拽方式开发ETL数据管道;可对接包括传统数据库、文件、大数据平台、接口、流数据等数据源;支持ETL数据管道加入机器学习算法。 主要功能 Pentaho D

weixin_43407520的博客 12万+

04-C#.Net-委托和事件-面试题

出题意图考察候选人对多播委托机制的理解,以及实际使用中的注意事项。解答思路解释多播委托的概念说明移除的机制和规则重点讲解移除的陷阱参考答案所有委托都继承自,因此所有委托都是多播委托。多播委托可以通过+=注册多个方法,形成一个调用链,执行时按注册顺序依次调用。使用-=操作符移除从后往前逐个匹配,匹配到第一个后移除并停止没有匹配到则不做任何操作// 陷阱1:实例方法必须是同一个实例// 失败:不是同一个实例// 陷阱2:Lambda 表达式无法移除。

噢~ 508

先进封装技术 Part03---重布线层(RDL)的科普

RDL是一种在芯片封装过程中用于重新分布电气连接的技术。它通过在芯片表面或中介层上形成额外的布线层,重新分配芯片的I/O(输入/输出)位置,以适应不同的封装需求和提高电气连接的灵活性。

阿拉伯梳子的专栏 4697

OpencvSharp学习笔记(一):图片的打开,保存灰度化,保存

近来萌生了学习OpenCv的想法,经过度娘得知老夫会的C#不能直接用OpenCv,只能用他的儿子OpenCvSharp与EmguCV,奈何奈何,这两玩意的资料少的出奇,只能慢慢的摸索了,EmguCV先不说,老夫找到了牛批的资料,先摸索OpencvSharp吧 首先打开VS通过Nuget安装 选择版本后点击安装就OK了. 安装完成后,引用里面就有以下的玩意儿了… 废话不多说了,在写程序前先 using OpenCvSharp 图片打开,灰度化,保存代码如下: static void Main(strin

猥琐de专注 7185

C# 中的委托和事件

转载自 http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html 委托和事件在 .NET Framework 中的应用非常广泛,然而,较好地理解委托和事件对很多接触 C# 时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里堵得慌,浑身不自在。本章中...

anmeidiao0254的博客 124

Shader Graph实战:5分钟搞定Flow Map水体流动效果(含UV偏移避坑指南)

本文详细解析了如何使用Unity的Shader Graph工具快速实现Flow Map水体流动效果,涵盖基础原理、核心节点搭建、高级技巧及性能优化。特别针对UV偏移问题提供避坑指南,帮助开发者轻松打造专业级动态水面效果,适合游戏场景增强视觉表现。

weixin_29250403的博客 244

C#委托与事件详解

本文转载自http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html C# 中的委托和事件(详解) C# 中的委托和事件        委托和事件在 .NET Framework 中的应用非常广泛,然而,较好地理解委托和事件对很多接触 C# 时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有...

快乐小羊~ 227

基于STM32的智能循迹小车设计(基础版)

基于STM32的智能循迹小车设计 硬件准备 1、小车底盘+四直流电机(带轮) 2、STM32F103C8T6核心板 3、12V 8700mAh锂电池(可以用几节18650锂电池) 4、循迹模块 选择的是TCRT500L,五路集成在一起,集成度比较高,但是可能不如五个单独的循迹模块好用 5、L298N电机驱动模块 四驱车为啥用L298N电机驱动模块? 可以把左边的两个电机并联,用输出A控制。把右边的两个电机并联,用输出B控制。 12V供电接电源正极,供电GND接电源负极和核心板GND,5V供电接核心

Rendezvous1的博客 3万+

升级最新IDEA版本(2022.1.1)

升级最新IDEA版本(2022.1.1)

软件老王 3万+

(转)C# 中的委托和事件(详解)1

转自https://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html 委托和事件在 .NET Framework 中的应用非常广泛,然而,较好地理解委托和事件对很多接触 C# 时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里堵得慌,浑身不自在。本章...

李小砍的技术博客 255

《测控仪器设计课后习题答案-浦昭邦-王宝光

《测控仪器设计课后习题答案-浦昭邦-王宝光 1.1. 测控仪器的概念是什么? 测控仪器则是利用测量和控制的理论,采用机、电、光各种计量测试原理及控制系统与计算机相结合的一种范围广泛的测量仪器。 1.2. 为什么说测控仪器的发展与科学技术发展密切相关? 仪器仪表的用途和重要性— 遍及国民经济各个部门,深入到人民生活的各个角落,仪器仪表中的计量测试仪器与控制仪器统称为测控仪器,可以说测控仪器的水平是科学技术现代化的重要标志。 仪器仪表的用途: 在机械制造业中:对产品的静态与动态性能测试;加工过程的控制与监测;设

无名何许人的博客 2976

【转】c#委托与事件

c#用委托来实现事件通知机制。委托相当与c++函数指针。整个过程涉及一个呼叫者,一个被呼叫者,还有就是这个委托。 - 实现步骤 有以下几步: 1. 申明委托, 2.定义呼叫者和调用的函数, 3.定义被呼叫者和具体实现的函数(被调用的函数) 1.申明委托 ,在包里或者类里, public publicdelegatevoidPlayGame(Object s...

aorhclxt606029的博客 132

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

C# 委托

目录1. 委托定义2. 实例化委托3. 内置委托4. 多播委托 1. 委托定义 2. 实例化委托 3. 内置委托 4. 多播委托

MechMaster 312

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【转】【UNITY3D 游戏开发之七】C# 中的委托、事件、匿名函数、Lambda 表达式

"委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。" from: http://www.himigame.com/unity3d-game/1613.html#header Unity3d 开发中,常用的莫过于委托和事件了,所以转载

青峰专栏 2813

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

C#版本与.NET版本对应关系以及各版本的特性

C#版本与.NET版本对应关系以及各版本的特性及修改C#语言版本方法

hpx666的博客 4091

C#.NET版本、Visual Studio版本对应关系

C#版本 .NET版本 Visual Studio版本 发布日期 特性 C# 1.0 .NET Framework 1.0 Visual Studio .NET 2002 2002-02-13 委托、事件 C# 1.1 .NET Framework 1.1 Visual Studio .NET 2003 2003-04-24 APM(异步编程模型) C# 2.0 .NET Framework 2.0 Vis...

Admans的专栏 2134

【UNITY3D 游戏开发之七】C# 中的委托、事件、匿名函数、Lambda 表达式

Unity3d 开发中,常用的莫过于委托和事件了,所以转载一篇相关文章,写的比较详细的,这里分享一下。 对于匿名函数以及Lambda表达式也是非常常用的,这里就直接分享链接,童鞋们自行学习。 匿名函数、Lambda表达式:http://www.cnblogs.com/yechun/archive/2012/04/15/2450496.html 【以下内

958

C# 与 .NET Framework 对应关系

C# 1.0 .NET Framework 1.0 2002-02-13 委托、事件 C# 1.1 .NET Framework 1.1 2003-04-24 ​ APM(异步编程模型) C# 2.0 .NET Framework 2.0 2005-11-07 VisualStudio 2005 泛型 分部类型 匿名方法 可以为 null 的值类型 迭代器 协变和逆变 泛型List<T>、匿名方法、迭代器yield、可空类型nullable C# 3.0 .NET Framew

anihasiyou的专栏 906

C# 中的委托和事件(简单易懂的讲解)

本文转载自http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html C# 中的委托和事件(详解) C# 中的委托和事件        委托和事件在 .NET Framework 中的应用非常广泛,然而,较好地理解委托和事件对很多接触 C# 时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有...

savagelin的专栏 398

C# .NET Framework的特殊委托

用追马的时间种草吧

weixin_43284350的博客 526

.net C#委托的个人见解

今天突然想到对委托的理解大致都忘了 就从新温习了一下 希望能帮到大家 首先看一下MSDN对委托的解释:                委托是一种定义方法签名的类型。 当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联。 您可以通过委托实例调用方法。 什么是委托  首先要知道什么是委托,用最通俗易懂的话来讲,你就可以把委托看成是用来执行方法(函数)的一个东西。 如何...

131

C#/.NET中委托和事件的机制和应用

 引自 http://www.cnblogs.com/stone/archive/2005/04/30/148129.aspx 本文摘自人民邮电出版社出版的《Windows Forms程序设计》(Chris Sells著,荣耀、蒋贤哲译)。通过一个栩栩如生的虚构故事解释了C#/.NET中委托和事件的机制和应用。      1 委托      从前,在南方的一个异国他乡,有一个叫P...

xineohpanihc 235
上一篇: 03-C#.Net-特性-面试题
下一篇: 04-C#.Net-委托和事件-面试题
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值