(精华)2020年8月11日 C#基础知识点 事件和委托
(精华)2020年8月11日 C#基础知识点 事件和委托
委托
委托太常见了,能灵活运用可以使你在编程中游刃有余。
简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名)
委托的简单使用
一个委托类型定义了该类型的实例能调用的一类方法,这些方法含有同样的返回类型和同样参数(类型和个数相同)。委托和接口一样,可以定义在类的外部。如下定义了一个委托类型 - Calculator: 此委托适用于任何有着int返回类型和一个int类型参数的方法,如: 创建一个委托实例,将该此方法赋值给该委托实例: 也可以简写成: 这个方法可以通过委托调用: 下面是完整代码:
用委托实现插件式编程
我们可以利用“委托是一个能把方法作为参数传递的对象”这一特点,来实现一种插件式编程。
例如,我们有一个Utility类,这个类实现一个通用方法(Calculate),用来执行任何有一个整型参数和整型返回值的方法。这样说有点抽象,下面来看一个例子:
如果Double方法是临时的,只调用一次,若在整个程序中不会有第二次调用,那么我们可以在Main方法中更简洁更灵活的使用这种插件式编程,无需先定义方法,使用λ表达式即可,如:
Utility.Calculate(values, x => x * 2);
以后我们会经常写这样的代码。
多播委托
所有的委托实例都有多播的功能。所谓多播,就像一群程序员在瞬聘网填好了求职意向后,某天有个公司发布了一个和这些程序员求职意向刚好相匹配的工作,然后这些求职者都被通知了 - “有一份好工作招人啦,你们可以直接申请去上班了!”。 MyDelegate d = MyMethod1;// “+=” 用来添加,同理“-=”用来移除。 d += MyMethod2;// d -= MyMethod2 MyDelegate d; d += MyMethod1;// 相当于MyDelegate d = MyMethod1; 我们先定义一个委托(ProgressReporter),然后定义一个匹配方法(Match)来执行该委托中的所有方法。如下: 然后我们需要两个监视进度的方法,一个把进度写到Console,另一个把进度写到文件。如下: 运行结果: 看到这里,是不是发现你已然更加爱上C#了。
静态方法和实例方法对于委托的区别
当一个类的实例的方法被赋给一个委托对象时,在上下文中不仅要维护这个方法,还要维护这个方法所在的实例。System.Delegate 类的Target属性指向的就是这个实例。举个例子: 但对于静态方法,System.Delegate 类的Target属性是Null,所以将静态方法赋值给委托时性能更优。
泛型委托
如果你知道泛型,那么就很容易理解泛型委托,说白了就是含有泛型参数的委托,例如: 我们可以把前面的例子改成泛型的例子,如下:
Func 和 Action 委托
有了泛型委托,就有了一能适用于任何返回类型和任意参数(类型和合理的个数)的通用委托,Func 和 Action。如下所示(下面的in表示参数,out表示返回结果): 有了这样的通用委托,我们上面的Calculator泛型委托就可以删掉了,示例就可以更简洁了: Func 和 Action 委托,除了ref参数和out参数,基本上能适用于任何泛型委托的场景,非常好用。
委托的兼容
1. 委托的类型兼容
delegate void D1();delegate void D2(); … D1 d1 = Method1; D2 d2 = d1; D2 d2 = newD2 (d1); delegate void D(); … D d1 = Method1; D d2 = Method1; Console.WriteLine (d1 == d2); // True
同理,对于多播委托,如果含有相同的方法和相同的顺序,也被视为相等。
2. 参数类型兼容
在OOP中,任何使用父类的地方均可以用子类代替,这个OOP思想对委托的参数同样有效。如:
3. 返回值类型兼容
道理和参数类型兼容一样:
事件
当我们使用委托场景时,我们很希望有这样两个角色出现:广播者和订阅者。我们需要这两个角色来实现订阅和广播这种很常见的场景。 事件就是描述这种场景模式的一个词。事件是委托的一个子集,为了满足“广播/订阅”模式的需求而生。
事件的基本使用
声明一个事件很简单,只需在声明一个委托对象时加上event关键字就行。如下: 事件的使用和委托完全一样,只是多了些约束。下面是一个简单的事件使用例子:
运行结果: 有人可能会问,如果把上面的event关键字拿掉,结果不是一样的吗,到底有何不同? 但事件有一系列规则和约束用以保证程序的安全可控,事件只有 += 和 -= 操作,这样订阅者只能有订阅或取消订阅操作,没有权限执行其它操作。如果是委托,那么订阅者就可以使用 = 来对委托对象重新赋值(其它订阅者全部被取消订阅),甚至将其设置为null,甚至订阅者还可以直接调用委托,这些都是很危险的操作,广播者就失去了独享控制权。
事件保证了程序的安全性和健壮性。
事件的标准模式
.NET 框架为事件编程定义了一个标准模式。设定这个标准是为了让.NET框架和用户代码保持一致。System.EventArgs是标准模式的核心,它是一个没有任何成员,用于传递事件参数的基类。 • 必须是 void 返回类型; • 它的名称必须以EventHandler结尾。 public delegate void EventHandler (object source, TEventArgs e) where TEventArgs : EventArgs; public delegate void PriceChangedEventHandler (object sender, PriceChangedEventArgs e); public class IPhone6 { … public event EventHandler PriceChanged; … }