• 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. 不允许包容类以外的类调用委托
  • 相关阅读:
    前端实现echarts折线图堆叠(多条折线)
    Golang GMP 原理
    【日常记录】解决‘GLIBC_2.34‘ not found,并且gcc制定glibc版本编译
    Java实现归并排序算法
    好玩好用软件推荐,让你大开眼界
    Go : for 语句简单使用
    《OpenHarmony开源鸿蒙学习入门》-- 状态管理
    NoSQL Redis
    机械设备行业数字化供应链集采平台解决方案:优化资源配置,实现降本增效
    cpacr_el1等特殊寄存器
  • 原文地址:https://blog.csdn.net/weixin_44771582/article/details/127506525