• C# 之委托和事件


    委托

    主要几个问题

    • 什么是委托
    • 怎么用
    • 什么是事件
    • 怎么用

    委托是什么

    委托,相当于在类中声明了一个方法类型,
    委托的主要一个应用就是,发布 - 订阅
    比如:

    // 发布者,感温器类,温度改变后,调用订阅者的订阅方法
    class Thermostat{
    	....
    	// 当温度改变时,调用的委托
    	public delegate void TemperatureChangeHandler(float newTemperature) 
    	// 相当于发布方法
    	public void onCurrent(float current){
    		// 改变当前温度
    		// 并且调用委托
    		TemperatureChangeHandler(current);
    	}
    	
    	....
    
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如上例子,相当于声明了一个参数为float类型,返回值为void的一个方法。
    再写一个类,用于给Thermostat类的委托方法赋值。

    // 订阅者,冷却器
    class Cooler{
    	....
    	// 订阅方法,也就是发布者发布时,调用的订阅者的订阅方法
    	public void onChanged(float tem){
    		console.write("onChanged");
    	}
    	....
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    使用委托

    Cooler cooler = new Cooler();
    Thermostat thermostat = new Thermostat();
    // 将cooler的订阅方法,注册到委托方法中
    thermostat.TemperatureChangeHandler +=cooler.onChanged
    thermostat.TemperatureChangeHandler +=cooler.onChanged
    
    • 1
    • 2
    • 3
    • 4
    • 5

    thermostat.TemperatureChangeHandler +=cooler.onChanged thermostat.TemperatureChangeHandler +=cooler.onChanged
    如此编码,便会形成一个委托的方法链,当发布者调用委托方法时,就会执行迭代地执行委托链中的订阅方法。

    Cooler cooler1 = new Cooler();
    Cooler cooler2 = new Cooler();
    Thermostat thermostat = new Thermostat();
    // 将cooler的订阅方法,注册到委托方法中
    thermostat.TemperatureChangeHandler +=cooler1 .onChanged
    thermostat.TemperatureChangeHandler +=cooler2.onChanged
    thermostat.onCurrent(15);
    // 如果要移出一个方法
    thermostat -=cooler1.onChanged
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    以上为不规范编码,切勿直接复制到编辑器,仅做例子。
    两个重点

    • 委托相当于声明一个参数为这样,返回值为这样的方法类型
    • 使用+=对委托方法赋值,多个方法形成委托链,如果使用 = 那么会对委托进行覆盖。

    关于异常

    假如委托链中的一个订阅方法引发了异常,那么链中的后续订阅方法就不能得到执行了,我们需要对委托方法进行异常的捕获,以使后续方法正常执行。

    // 如在发布者的发布方法中
    public delegate void OnChangedHandler(float tem)
    public void publish(float tem){
    	// 可通过委托的GetInvocationList()去获取委托链
    	foreach(OnChangedHandler handler in OnChangedHandler.GetInvocationList()){
    	// 捕获
    	try{
    	handler(tem)
    	
    }catch(Exception e){
    	....
    }
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    假设我们的订阅方法有返回值…

    我们需要和处理异常的方法一致,需要调用委托的GetInvocationList方法来获取委托链。

    事件

    为何会有event事件?

    委托的封装不充分

    比如

    // 一个感温器类
    class Thermostat{
    
    	// 委托方法
    	public delegate void TemperatureChangeHandler(float newTemperature) 
    	private float _Current;
    	// 修改/获取感温器当前温度
    	public float Current(float current){
    		// 改变当前温度
    		// 并且调用委托
    		get{
    			return _Current;
    		}
    		set{
    			// 调用委托,发布通知到订阅者
    			TemperatureChangeHandler(value);
    			// 修改感温器的温度
    			_Current = value;
    		}
    		
    	}
    }
    // 在main函数中
    static void Main(string[] args)
    {
       // 创建对象,注册订阅方法,省略
       ....
       // 调用委托
       Thermostat.TemperatureChangeHandler(123);
       
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    如果,我们可以直接在感温器类的外部直接调用委托方法机进行通知,而不是调用感温器类的Current方法去修改感温器类的温度,然后再调用委托发送通知。那么这样直接在外部调用委托方法,所导致的就是,感温器的温度并没有被改变,但是却发送了通知给所有的订阅者。

    并且,并且
    我们可以直接在外部使用 =+=-=对委托进行修改。

    这两个问题都是委托的封装不充分的体现。

    事件的使用

    public delegate void TemperatureChangeHandler(float tem);
    public event TemperatureChangeHandler onTemperatureChange = delegate {}
    
    • 1
    • 2

    这样去声明了一个委托的事件之后,就是声明这个委托是一个事件(事实上,声明了之后,编译之后会生成一些限制代码)
    会有两个作用:

    1. 禁止对一个public委托使用赋值运算符
    2. 不允许包容类以外的类调用委托
  • 相关阅读:
    C++程序设计——类和对象(上)
    spring_注解笔记
    验证码绕过、密码找回漏洞
    oracle查询相同条件重复值只取第1条
    俄罗斯方块c语言
    OccuSeg: Occupancy-aware 3D Instance Segmentation
    spring-boot
    02 Java虚拟机的结构
    【正点原子FPGA连载】 第三章 硬件资源详解 摘自【正点原子】DFZU2EG/4EV MPSoC 之FPGA开发指南V1.0
    Windows------openvino 2022.1安装步骤
  • 原文地址:https://blog.csdn.net/weixin_44771582/article/details/127506525