- A+
所属分类:.NET技术
委托与事件
委托
委托的定义
委托是C#中的一种类型,用于存储对方法的引用。它允许将方法作为参数传递给其他方法,实现回调、事件处理和动态调用等功能。通俗来讲,就是委托包含方法的内存地址,方法匹配与委托相同的签名,因此通过使用正确的参数类型来调用方法。
委托的特性
- 引用方法:委托允许存储对方法的引用,使得方法可以被动态地调用。
- 类型安全:委托是类型安全的,它们在编译时会检查方法签名,确保委托实例只能引用与其声明的相同签名的方法。
- 多播性:委托支持多播,即一个委托实例可以引用多个方法。通过 += 和 -= 操作符,可以动态地添加或移除委托链中的方法。
- 异步编程:委托在异步编程中扮演了重要的角色,尤其是在使用 BeginInvoke 和 EndInvoke 进行异步操作时。
- 匿名方法和 Lambda 表达式:C# 支持使用匿名方法和 Lambda 表达式来创建简洁的委托实例,减少了样板代码的编写。
- 委托泛型化:C# 提供了泛型委托 Action 和 Func,分别用于表示没有返回值和有返回值的委托,减少了需要定义新委托类型的情况。
查看代码
class Program { static void Main(string[] args) { Publisher publisher = new Publisher("篮球先锋报"); Observer observerA = new Observer("老A"); publisher.Magazine += observerA.RecvMagazine; Observer observerB = new Observer("老B"); publisher.Magazine += observerB.RecvMagazine; publisher.PublishMagezine(); //或者使用下面的方式 区别就是一个在定义的内部触发,一个在外部触发 publisher.Magazine?.Invoke(publisher.magazineName); Console.ReadKey(); } } public class Observer { private string name; public Observer(string name) { this.name = name; } public void RecvMagazine(string message) { Console.WriteLine($"{this.name} recv {message}, 仔细读了一番"); } } public class Publisher { public string magazineName; public Publisher(string magazineName) { this.magazineName = magazineName; } public delegate void MagazineDelegate(string message); public MagazineDelegate Magazine; public void PublishMagezine() { Magazine?.Invoke(this.magazineName); } }
事件
查看代码
public class EventPublisher { // 1. 定义委托类型 public delegate void MyEventHandler(object sender, EventArgs e); // 2. 声明事件,使用 event 关键字 public event MyEventHandler MyEvent; // 3. 编写引发事件的方法 protected virtual void OnMyEvent() { // 4. 引发事件,安全调用委托 MyEvent?.Invoke(this, EventArgs.Empty); } }
查看代码
class Program { static void Main(string[] args) { Publisher publisher = new Publisher("篮球先锋报"); Observer observerA = new Observer("老A"); publisher.Magazine += observerA.RecvMagazine; Observer observerB = new Observer("老B"); publisher.Magazine += observerB.RecvMagazine; publisher.PublishMagezine(); //下面的方式会出现编译错误 只允许在定义的内部触发,不允许在外部触发 publisher.Magazine?.Invoke(publisher.magazineName); Console.ReadKey(); } } public class Observer { private string name; public Observer(string name) { this.name = name; } public void RecvMagazine(string message) { Console.WriteLine($"{this.name} recv {message}, 仔细读了一番"); } } public class Publisher { public string magazineName; public Publisher(string magazineName) { this.magazineName = magazineName; } public delegate void MagazineDelegate(string message); public event MagazineDelegate Magazine; public void PublishMagezine() { Magazine?.Invoke(this.magazineName); } }
经典面试题
猫叫、老鼠跑了,主人醒来了
查看代码
class Program { static void Main(string[] args) { Cat cat = new Cat(); Mouse m = new Mouse(cat); People p = new People(cat); cat.Scream(); } } public class Cat { public delegate void ScreamHandler(); public event ScreamHandler OnScream; public void Scream() { Console.WriteLine("猫叫了一声"); OnScream?.Invoke(); } } public class Mouse { public Mouse(Cat c) { c.OnScream += () => { Console.WriteLine("老鼠跑了"); }; } } public class People { public People(Cat c) { c.OnScream += () => { Console.WriteLine("主人醒来了"); }; } } //输出: //猫叫了一声 //老鼠跑了 //主人醒来了
委托与事件的区别
- 事件基于委托,但并非委托 可以把事件看成委托的代理。在使用者看来,只有事件,而没有委托。
- 事件是对委托的包装 保护委托字段,对外不开放。所以外部对象没法直接操作委托。提供了 Add 和 Remove 方法,供外部对象订阅事件和取消事件。事件的处理方法在对象外部定义,而事件的执行是在对象的内部。至于事件的触发,何时何地无所谓。
什么时候使用委托与事件?
如果一个委托不需要再其定义的类之外被触发,那么就可以将其转化为事件,这样可以保证它不会在外部被随意触发。