C# 委托与事件 深入

  • 委托
    • 委托最大的好处
    • 委托的第二个作用就是可以多播
    • 泛型委托
    • 预定义的委托
      • *`EventHandler` 委托:*
  • 事件
    • 事件就是一种特殊的委托
    • 举个例子
    • 微软为我们提供了标准事件模式

委托

这是一个简单的委托样例

class TODO {public static void Main(String[] args) {Cal aa = new Cal(Add);Console.WriteLine(aa(2, 3));}public delegate int Cal(int a, int b);private static int Add(int a, int b) {return a + b;}private static int Dec(int a, int b) {return a - b;}}

如此简单的应用就是函数指针的意思
委托就是我给你原材料,你给我我要的成品,至于你拿我原材料干啥,我不是深究。

于是,有一个问题,为什么我不直接调用Add或者Dec函数呢?
这就是

委托最大的好处

变量分离,将不变的封装起来,隔离变化,例子如下:

class TODO {public static void Main(String[] args) {test(Add, 2, 3, 4);}public delegate int Cal(int a, int b);internal static void test(Cal cc, int a, int b, int c) {int x = 0;int y = 0;if (a > b) {if (a > c) {x = a;} else {if (b > c) {y = b;} else {y = c;}}} else {if (b > c) {x = b;} else {x = c;if (a > b) {y = a;} else {y = b;}}}int result = cc(x, y);Console.WriteLine(result);}private static int Add(int a, int b) {return a + b;}private static int Dec(int a, int b) {return a - b;}}

我有一个方案test
他的一切都是固定下来的,但是他的计算方法是多变的,因为我们可以用委托来改变传入的方法
于是我们在不改变test的情况下让他有了更多的功能

委托的第二个作用就是可以多播

就是一个委托实例可以添加多个函数,当然也能删除,要是都删了,委托new的实例就是null
代码如下:

class TODO {public static void Main(String[] args) {Foo f = new Foo(f1);f.Invoke();Console.WriteLine("-------------------");f += f2;f += f3;f();Console.WriteLine("-------------------");f -= f1;f -= f3;f += f1;f();Console.WriteLine("-------------------");f -= f1;f -= f2;if(f == null) { Console.WriteLine("null"); } else {Console.WriteLine("unnull");}Console.WriteLine("-------------------");f?.Invoke();Console.WriteLine("-------------------");}internal delegate void Foo();private static void f1() { Console.WriteLine("f1"); }private static void f2() { Console.WriteLine("f2"); }private static void f3() { Console.WriteLine("f3"); }
}

C#中,f?.Invoke(); 是一个使用null条件运算符(也称为Elvis运算符)的表达式。这个表达式用于安全地调用一个可能为null的委托(delegatef
这里的 ?. 运算符检查 f 是否为 null。如果 f 不是 null,则 Invoke 方法会被调用;如果 fnull,则整个表达式不会有任何效果(即不会抛出异常,也不会执行任何操作)。
这种语法特别有用,因为它可以避免在尝试调用一个null引用的方法时抛出 NullReferenceException 异常。

总之就是判断是否为null,不是就正常执行,是就无事发生。

打印结果为:

f1
-------------------
f1
f2
f3
-------------------
f2
f1
-------------------
null
-------------------
-------------------

注意
委托的多播最后的返回值是最后加入的函数的返回值

class TODO {public static void Main(String[] args) {Foo f = new Foo(f1);Console.WriteLine(f(2));f += f2;Console.WriteLine(f(3));}delegate string Foo(int x);static string f1(int i) {return (i+1).ToString();}static string f2(int i) {return (i+2).ToString();}}

运行结果为:

3
5

泛型委托

delegate T NumberChanger<T>(T n);
class TODO {public static void Main(String[] args) {//声明实例TODO doit = new TODO();//声明委托实例,确定模板的类型为intRule<int> rule = new Rule<int>(doit.MsRule);//通过实例调用其成员Console.WriteLine(doit.PickOne<int>(1, 2, rule));}//模板函数 接收两个类型为T的参数和一个Rule<T>类型的委托作为参数,//然后调用这个委托并返回其结果private T PickOne<T>(T a, T b, Rule<T> rule) {return rule(a, b);}//接收两个整数参数,并返回较大的那个整数。public int MsRule(int a, int b) {return a > b ? a : b;}
}//泛型委托Rule<T>,它接收两个类型为T的参数,并返回一个类型为T的结果。
delegate T Rule<T>(T a, T b);

注意:上述代码没有使用静态static,故要通过实例调度,即非静态方法是依附于类的实例,必须通过实例来调用

预定义的委托

然后我们再想,既然都用泛型委托了,其实对于声明一个委托来说,委托的名字叫什么并不重要,重要的是确定他的返回值类型和参数列表

所以每次要声明一个委托模板就显得有点繁琐,故:

.NET中,委托是一种类型安全的函数指针,它允许将方法作为参数传递或赋值给变量。委托定义了一种方法的签名,即它接受的参数类型和返回类型。任何与委托签名匹配的方法都可以赋值给该委托的变量。

.NET框架提供了多种预定义的委托类型,用于处理常见的场景。以下是一些常用的.NET委托:

Action 委托:
Action 委托用于表示没有返回值(即返回类型为 void)的方法。Action 有多个重载版本,可以接受不同数量的参数,例如:

Action:无参数。
Action<T>:接受一个参数。
Action<T1, T2>:接受两个参数。
以此类推,直到 Action<T1, T2, ..., T16>

Func 委托:
Func 委托用于表示有返回值的方法。与 Action 类似,Func 也有多个重载版本,每个版本都指定了返回类型和一个或多个输入参数类型,例如:

Func<TResult>:无参数,返回 TResult 类型的结果。
Func<T, TResult>:接受一个类型为 T 的参数,返回 TResult 类型的结果。
Func<T1, T2, TResult>:接受两个参数,返回 TResult 类型的结果。
以此类推,直到 Func<T1, T2, ..., T16, TResult>

Predicate 委托:
Predicate<T> 委托用于定义返回 bool 类型的方法,它接受一个类型为 T 的参数。这通常用于需要条件测试的场景,例如数组或集合中的元素筛选。

Comparison 委托:
Comparison<T> 委托用于定义比较两个类型为 T 的对象的方法,并返回一个整数来表示它们的相对顺序。这常用于排序操作。

EventHandler 委托:

EventHandlerEventHandler<TEventArgs> 是用于事件处理的委托。在.NET中,事件通常基于这些委托来定义。EventHandler 没有参数,而 EventHandler<TEventArgs> 接受一个 TEventArgs 类型的参数,其中 TEventArgs 通常是从 EventArgs 类派生的自定义事件参数类。

Converter 委托:
Converter<TInput, TOutput> 委托用于定义将一种类型转换为另一种类型的方法。它接受一个类型为 TInput 的参数,并返回一个类型为 TOutput 的结果。

这些预定义的委托类型大大简化了代码,特别是在需要传递方法作为参数或回调时。你可以直接使用这些委托,而不必每次都定义自己的委托类型。当然,如果需要更复杂的签名或特定的行为,你仍然可以定义自己的委托类型。

接下来就是事件

事件

事件就是一种特殊的委托

事件的作用就是可以在委托实例化的之后不需要立刻为其注册一个方法:

类似于函数指针赋值为NULL
类似于窗口程序中我放了一个button,但是这个button什么都不做。

那么事件怎么声明呢,事件要用委托的类型来声明

class TODO {public static void Main(String[] args) {event Mydelegate Click;}delegate void Mydelegate();
}

可以理解成我实例化了Mydelegate这个委托,实例化的名字是Click,而且没有绑定东西

当然我们可以使用预定义的委托

class TODO {private event Action<int> Click;public static void Main(string[] args) {TODO doit = new TODO();doit.Click += (x) => { Console.WriteLine(x); };doit.Click(2);//非法调用不可取}
}

比较好的写法:

using System;class TODO {// 定义一个带有一个int类型参数的Action委托类型的事件  public event Action<int> Click;// 受保护的方法用于触发Click事件  protected virtual void OnClick(int value) {// 检查是否有订阅者  Click?.Invoke(value);}// 公共方法,允许外部调用者触发事件  public void TriggerClick(int value) {OnClick(value);}
}class Program {static void Main(string[] args) {// 创建TODO类的实例  TODO doit = new TODO();// 订阅第一个委托到Click事件  doit.Click += (x) => { Console.WriteLine("第一个委托被调用: " + x); };// 订阅第二个委托到Click事件  doit.Click += (x) => { Console.WriteLine("第二个委托被调用: " + x); };// 通过TODO类的实例方法来触发事件  doit.TriggerClick(1); // 这会依次调用所有订阅了Click事件的处理程序  }
}

其中

 protected virtual void OnClick(int value) {// 检查是否有订阅者  Click?.Invoke(value);}

C#中,?. 是空条件运算符(null-conditional operator)的语法。这个运算符用于在访问对象的成员(属性、方法或索引器)之前检查该对象是否为null。如果对象为null,则整个表达式会立即返回null,而不会引发NullReferenceException异常。

空条件运算符提供了一种简洁的语法来避免在访问对象成员之前进行显式的null检查。这可以使代码更简洁,并减少由于忘记进行null检查而导致的运行时错误。

C# 中,事件是特殊的成员,它们只能由定义它们的类内部触发,而不能从类的外部直接调用。

举个例子

class demo {private int a;public int A {get { return a; }set { a = value;aChanged?.Invoke();}}public event Action aChanged;}class TODO {public static void Main() {demo dd = new demo();dd.aChanged += () => Console.WriteLine("a was changed.");dd.A = 1;dd.A += 2;}
}

输出:

a was changed.
a was changed.

微软为我们提供了标准事件模式

提供了一个委托类型:EventHandler
返回值void
两个参数objectEventArgs

object是触发事件的对象本身,EventArgs是触发事件包含的信息

统一特征

 (object sender, EventArgs e)

命名小寄巧
名字 + 动词过去式
On + 名词 + 动词过去式
Raise + 名词 + 动词
动词

EventRised
PropertyChanged
CollectionChangedOnPropertyChangedRaisePropertyChangeClick
KeyDown

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/794268.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 线程:线程同步、生产者消费者模型

目录 一、死锁 二、条件变量实现线程同步 1、为什么需要线程同步 2、条件变量、同步、竞态条件 3、条件变量函数&#xff1a;初始化 销毁 等待 唤醒 4、实现简单的多线程程序 不唤醒则一直等待 实现线程同步 三、生产者消费者 1、借助超市模型理解 2、优点 四、基于…

Java基础知识总结(39)

1、今天学了什么 &#xff08;1&#xff09;构造器 构造器的定义&#xff1a; 需要注意的是构造器是一种特殊的方法&#xff0c;其方法名和类名相同&#xff0c;但没有方法返回值&#xff0c;也不用void修饰。 [修饰符] 方法名(形参列表){方法体 } 修饰符&#xff1a;修饰符可…

数字乡村创新实践探索:科技赋能农业现代化与乡村治理体系现代化同步推进

随着信息技术的飞速发展&#xff0c;数字乡村作为乡村振兴的重要战略方向&#xff0c;正日益成为推动农业现代化和乡村治理体系现代化的关键力量。科技赋能下的数字乡村&#xff0c;不仅提高了农业生产的效率和品质&#xff0c;也为乡村治理带来了新的机遇和挑战。本文旨在探讨…

Linux 环境下 Redis基础配置及开机自启

Linux 环境下 Redis基础配置及开机自启 linux环境安装redis<redis-6.0.5.tar.gz> 1-redis基本安装配置 解压 获取到tar包后&#xff0c;解压到相关目录&#xff0c;一般是将redis目录放在usr/local/redis目录下&#xff0c;可以使用-C指定到解压下目录 tar -zvxf re…

Java数据结构栈

栈&#xff08;Stack&#xff09; 概念 栈是一种先进后出的数据结构。 栈的使用 import java.util.Stack; public class Test {public static void main(String[] args) {Stack<Integer> s new Stack();s.push(1);s.push(2);s.push(3);s.push(4);System.out.println(s…

3. python练习题3-自由落体

3. python练习题3-自由落体 【目录】 文章目录 3. python练习题3-自由落体1. 目标任务2. 解题思路3. 知识回顾-%占位符格式化处理3.1 概述3.2 占位符的多种用法3.3 格式化操作符辅助指令3.4 将整数和浮点数格式化为字符串 4. 解题思路4.1 球第1次下落4.2 球第2次下落 5. 最终代…

day60 动态规划part17

这两题看了自己写的笔记还不懂的话&#xff0c;看看这个up的思路就行&#xff1a; https://space.bilibili.com/111062940/search/video?keyword%E5%9B%9E%E6%96%87 647. 回文子串 中等 提示 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回…

学习【RabbitMQ入门】这一篇就够了

目录 1. RabbitMQ入门1-1. 同步调用1-2. 异步调用1-3. MQ技术选型1-4. RabbitMQ介绍消息模式 1-5. SpringAMQPBasic QueueWork QueueFanout ExchangeDirect ExchangeTopic Exchange消息转换器 1. RabbitMQ入门 1-1. 同步调用 优势&#xff1a; 时效性强&#xff0c;等待到结…

Invarient facial recongnition

for later~ 代码阅读 1. 加载trainset import argparse import logging import os import numpy as npimport torch from torch import distributed from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriterfrom backbones import get_…

Windows 2008虚拟机安装、安装VM Tools、快照和链接克隆、添加硬盘修改格式为GPT

一、安装vmware workstation软件 VMware workstation的安装介质&#xff0c;获取路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1AUAw_--yjZAUPbsR7StOJQ 提取码&#xff1a;umz1 所在目录&#xff1a;\vmware\VMware workstation 15.1.0 1.找到百度网盘中vmwa…

Mysql的基本命令

1 服务相关命令 命令描述systemctl status mysql查看MySQL服务的状态systemctl stop mysql停止MySQL服务systemctl start mysql启动MySQL服务systemctl restart mysql重启MySQL服务ps -ef | grep mysql查看mysql的进程mysql -uroot -hlocalhost -p123456登录MySQLhelp显示MySQ…

C++初阶:vector类的模拟实现(含模板)

目录 1.Vector.h 2.Test.cpp 1.Vector.h #include <iostream> #include <cassert> #include <algorithm> using namespace std; template <class T> class Vector { public:typedef T *iterator;typedef const T *const_iterator;iterator begin() …

8.list容器的使用

文章目录 list容器1.构造函数代码工程运行结果 2.赋值和交换代码工程运行结果 3.大小操作代码工程运行结果 4.插入和删除代码工程运行结果 5.数据存取工程代码运行结果 6.反转和排序代码工程运行结果 list容器 1.构造函数 /*1.默认构造-无参构造*/ /*2.通过区间的方式进行构造…

java-网络编程socket-聊天室-03

完整版代码 java -聊天室的代码: 用于存放聊天室的项目的代码和思路导图https://gitee.com/to-uphold-justice-for-others/java---code-for-chat-rooms.git 多线程并发问题 多线程的并发问题主要出现在当一个程序涉及多个线程同时运行时&#xff0c;这些线程可能会同时访问共…

conda删除环境指令

要删除Conda环境&#xff0c;你可以使用下面的指令&#xff1a; conda remove --name your_env_name --all这里的 your_env_name 是你想要删除的环境名称。这条指令会删除指定的环境及其下的所有包。 如果你正在使用的是Anaconda Prompt或者是命令行界面&#xff0c;确保你有…

Java:接口应用(Clonable 接口和深拷贝)

目录 1.引例2.Object中clone方法的实现3.Cloneable接口讲解4.深拷贝和浅拷贝4.1浅拷贝4.2深拷贝 1.引例 Java 中内置了一些很有用的接口, Clonable 就是其中之一. Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法。必…

精密电阻阻值表和电容容值表

前面2张是电阻阻值表&#xff08;E-96/0603/1%&#xff09; 常见贴片电容的容值表

解决windows下Qt Creator显示界面过大的问题

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 问题描述 解决方法 1、右击此电脑--->属性 2、点击高级系统设置--->点击环境变量 3、 找到系…

【美团笔试题汇总】2023-08-26-美团春秋招笔试题-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新小米近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…

想要安装ssh?

SSH&#xff08;Secure Shell&#xff09;是一种加密的网络协议&#xff0c;用于在不安全的网络上安全地进行远程登录和执行命令。它通过加密通信和身份验证机制&#xff0c;确保用户和系统之间的通信是安全的。 SSH协议的主要功能包括&#xff1a; 加密通信&#xff1a;SSH使…