04-C#

C# NanoFramework使用ESP32开发超声波距离传感器(HC-SR04) 阅读详情

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

一、委托(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 记忆口诀

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

【Unity 04】C# 基础类型 PS:本系列笔记将会记录我此次在北京学习Unity开发的总体过程,方便后期写总结,笔记为天更。 笔记内容均为 自己理解,不保证每个都对。 这个Blog后会加入C#的算法题栏目。。。就当练手了。 Part 1 创建 C#项目: VS选择新建项目 C# ➡ 控制台应用 ➡ 修改目录与文件名 ➡ 确定 Part 2 基础简介 C#与.Net区别: .net/dontnet:一般指.net frame... 阅读详情

相关推荐

C# Modbus通信从入门到精通(25)——Modbus TCP(0x04功能码与C#代码实现)

本文详细介绍了ModbusTCP协议中0x04读输入寄存器功能码的原理、发送报文、返回报文,由高级工程师多年经验总结而成,绝对干货!

qq_34059233的博客 2429

Ubuntu16.04安装MonoDevelop运行C#代码

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34268843的博客 524

C# Modbus通信从入门到精通(6)——Modbus RTU(0x04功能码与C#代码实现)

本文详细介绍了ModbusRTU协议中0x04读输入寄存器功能码的原理、发送报文、返回报文,由高级工程师多年经验总结而成,绝对干货!

qq_34059233的博客 3123

c# .net8 香橙派orangepi + hc-04蓝牙 实例

这些使用c# .net8开发,硬件 香橙派 orangepi 3lts和 hc-04蓝牙。使用场景:可以通过这个功能,手机连接orangepi进行wifi等参数配置。在香橙派orangepi 上运行,接收到手机APP调试工具发送的内容。1、带USB口的linux开发板orangepi。某宝上买的蓝牙官方网有调试工具:HC-T串口助手。2、USB 转TTL 中转接蓝牙(HC-04)电脑和手机上调试成功后,证明蓝牙是正常使用的。也有手机端的调试工具。

qq_16005627的博客 1293

C# Modbus通信从入门到精通(16)——Modbus ASCII(0x04功能码与C#代码实现)

本文详细介绍了ModbusASCII协议中0x04读输入线圈功能码的原理、发送报文、返回报文、c#代码实现,由高级工程师多年经验总结而成,绝对干货!

qq_34059233的博客 1400

PyQt5 使用QSqlDatabase连接Mysql数据库时Driver not loaded Driver not loaded

PyQt5 使用QSqlDatabase连接Mysql数据库时Driver not loaded Driver not loaded

睡觉不打呼噜的博客 1562

Ubuntu16.04安装Mono、MonoDevelop运行C#代码

Ubuntu16.04安装MonoDevelop运行C#代码 在Ubuntu上安装Mono 运行下面代码授权注册repo源并更新软件列表: Add the Mono repository to your system The package repository hosts the packages you need, add it with the following commands....

weixin_30919235的博客 634

用CesiumLab2+nginx零成本构建三维地形服务:从DEM数据到浏览器渲染全流程

本文详细介绍了如何利用CesiumLab2和nginx零成本构建私有三维地形服务的完整流程。从获取DEM数据、使用CesiumLab2进行格式转换,到配置nginx服务器并重点解决跨域问题(Access-Control-Allow-Origin),最终实现在Cesium中流畅加载与渲染自定义地形,为内网或涉密项目提供了高性价比的解决方案。

weixin_29300931的博客 117

在VS2013的C#工程中使用Tesseract3.04

 Tesseract3.04是Tesseract目前最新的版本,该开源项目的家已经从Google Code搬到了Github。今天在VS2013的C#项目中简单验证了Tesseract3.04的功能,在这里记录一下,供有兴趣的同学参考。1. 参考资源说明      Tesseract开源项目:https://github.com/tesseract-ocr/tesseract,目前该项目中包含Te...

imxiangzi的专栏 3385

论文记录:Affine Formation Maneuver Control of Multiagent Systems

多智能体编队控制仿真记录 实现功能:平移/旋转/尺寸缩放/shear,见截图 注意:本仿真图只是起到了演示效果,中间过程速度产生突变,实际中是不允许的。需要进行速度规划,这一点论文中仿真图更为贴合实际。 参考论文:Zhao, S. (2018). Affine Formation Maneuver Control of Multiagent Systems. IEEE Transactions on Automatic Control, 63(12), 4140–4155. 突然发现有个博主写得很好,参考:

W1H1J1的博客 2107

蓝牙HCI Dongle实战:从芯片选型到协议栈集成的完整指南

本文详细介绍了蓝牙HCI Dongle的实战应用,从芯片选型到协议栈集成的完整指南。通过对比CSR8510、nRF52840等主流芯片的性能和价格,提供选型建议和避坑技巧。同时,深入解析Zephyr协议栈移植和常见问题排查,帮助开发者高效完成蓝牙项目开发。

weixin_30786657的博客 321

C#基础04

介绍:泛型介绍,索引,Foreach遍历的解释,yield方法,path文件操作,Directory类基本操作<目录> 一:泛型 百度资料:泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。泛型将类型参数的概念引入 .NET Framework,类...

chuhe8332的博客 112

K8S部署Java项目 pod的logs报错为:Error: Unable to access jarfile app.jar

每个人都有惰性,但不断学习是好好生活的根本,共勉!

感谢阅读 2600

Ubuntu18.04 安装vscode 配置C#编译器

文件–打开文件夹–终端dotnet new console —新建XXX.cs文件–调试(甲壳虫),按说明选择编译器—运行,修改launch.json文件。文件–打开文件夹–终端dotnet new console —新建XXX.cs文件–终端dotnet run。看到这个提示就是安装好了,接着添加环境变量,也就是链接库。然后新建一个C#工程,在终端测试一下看是否能够编译。新建文件夹(projectName)

little_baby2的博客 1679

2024年全国青少年信息素养大赛初赛真题(算法创意实践挑战赛C++小学组:带解析)

2024全国青少年信息素养大赛C++算法创意实践挑战赛初赛真题及解析

王老师青少年编程 2806

学Simulink——基于高渗透率可再生能源微电网的逆变器系统场景实例:含高比例逆变型电源的微电网暂态稳定性分析

本文详细介绍了基于MATLAB/Simulink的高渗透率可再生能源微电网建模方法,重点分析含高比例逆变型电源的微电网暂态稳定性问题。通过构建包含光伏、风电、逆变器和电网等模块的系统模型,展示了从项目创建、模块搭建到控制策略实现的完整过程。文章特别提供了逆变器PQ控制算法和故障处理逻辑的代码示例,并介绍了仿真参数设置与结果分析方法。该模型能有效评估微电网在故障条件下的动态响应特性,为优化逆变器控制策略提供依据,对现代智能电网研究具有重要意义。

xiaoheshang_123的博客 140

C#学习——Day04

<dt> <dd> </dd> </dt>

lb121zc的博客 674

【WorkBuddy专栏58】腾讯为什么要「自己打自己」——7款AI智能体产品全景对比与「赛马」逻辑深度拆解

【WorkBuddy专栏57】你的左侧空间不是「日任务清单」——工作空间才是 WB 变聪明的核心秘密

AI人人懂 670

Ubuntu 20.04.6 LTS如何安装Lua和C#

本文详细介绍了在Ubuntu 20.04上安装Lua和C#的多种方法。对于Lua,提供了APT安装和源码编译两种方式;针对C#,给出了.NET SDK、Mono和.NET Core三种安装方案。文中包含详细的安装命令、验证步骤以及开发工具推荐,并附带了环境配置提示,包括版本管理、路径设置和权限处理等实用建议。这些方法能满足不同开发需求,帮助用户快速搭建Lua和C#的开发环境。

小宝哥Code的专栏 294

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

PLC通讯实现-C#访问MxComponent4.16S实现读写三菱PLC-R04(十一)

PLC通讯实现-C#访问MxComponent4.16S实现读写三菱PLC-R04(十一)背景注意点关键代码手册下载代码下载 背景 由于PLC型号在不断的更新换代,相关配套软件和通讯方式也要相应更新才行,近期项目中使用了三菱比较新型的R系列PLC(R04),原来使用MXComponent3下的ActMulti.dll就不支持了,因此需要升级为最新的MXComponent4.16S中的ActUtlT...

分享技术,传播知识! 1万+

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

ubuntu 16.04下用.NET Core写C#程序

2017 微软一个比较大的动作就是.Net core2.0了,微软出的这个框架野心很大:它可以让用户在几乎所有的PC终端用C#编写自己的应用程序。在win/linux/mac三大主流操作系统通吃之后,.Net core居然也支持Docker! 本文试着在ubuntu 16.04运行自己的C#程序也,其他操作系统包括Windows,MacOS和Docker上的教程见官网:.Net Core 首先注

xiangxianghehe的博客 7156

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C#学习DAY04

在类的所有实例中共享,并且可以通过类名直接访问,而不需要创建类的实例。1)局部作用域:变量声明在函数,方法,块(循环或条件语句等)的内部,仅在块的内部可访问。如果复用我们习惯使用静态的,如果各自独立使用,也不需要立即使用,用的时候在创建,我们习惯使用实例变量。public void SetMember(int value)//private类中的公用类,便于外部访问。2)类作用域:定义在在类内部但在方法、构造函数或属性之外声明的变量。

ycb1746147428的博客 1234

C#入门记录04

这一部分主要记录下运算符相关的东西 /* 2020年3月4日22:28:49 运算符相关的东西 使用Math来进行相关的数据计算 */ using System; namespace ConsoleApp4 { class Program { static void Main(string[] args) { ...

JamesWu9527的博客 286
上一篇: 02-C#
下一篇: 12-C#
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值