#unity/日常积累 ## C#深入理解委托和事件的区别 ### 1、委托 ``` cs 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 MagazineDelegate Magazine;),然后再这个类的外部订阅它(publisher.Magazine += observerA.RecvMagazine;)。但是我们在触发这个委托的时候,可以在内部中触发(就是用一个方法封装起来公开给外部调用): ``` cs public void PublishMagezine() { Magazine?.Invoke(this.magazineName); } ``` 也可以在外部直接触发: ``` cs publisher.Magazine?.Invoke(publisher.magazineName); ``` ### 2、事件 ``` cs 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); } } ``` 我们将委托换为事件后发现,不能够直接在外部触发。 ``` cs publisher.Magazine?.Invoke(publisher.magazineName); ``` 这一句直接报错,编译不通过,即使该事件是public的。 ### 3、标准的事件 参考[[EventArgs 类]] ``` cs 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(); Console.ReadKey(); } } public class Observer { private string name; public Observer(string name) { this.name = name; } public void RecvMagazine(object sender,Publisher.MagazineMessageEventArgs e) { Console.WriteLine($"{this.name} recv {e.messgae}, 仔细读了一番"); } } public class Publisher { public class MagazineMessageEventArgs : EventArgs { public string messgae; public MagazineMessageEventArgs(string msg) { this.messgae = msg; } } public string magazineName; public Publisher(string magazineName) { this.magazineName = magazineName; } public event EventHandler Magazine; public void PublishMagezine() { Magazine?.Invoke(this,new MagazineMessageEventArgs(this.magazineName)); } } ``` 使用了系统自定义的事件及参数类型。其实,很多教程中写道public delegate void EventHandler(object sender, TEventArgs e);中的TEventArgs必须要继承自EventArgs,但是在Framework4.8中我并没有看到那个限定, 所以TEventArgs可以是任何类型,public delegate void EventHandler(object sender, TEventArgs e);就是系统自定义好的一个泛型委托。 我们可以直接将MagazineMessageEventArgs类型改为string类型更加简单。 ``` cs 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(); Console.ReadKey(); } } public class Observer { private string name; public Observer(string name) { this.name = name; } public void RecvMagazine(object sender, string msg) { Console.WriteLine($"{this.name} recv {msg}, 仔细读了一番"); } } public class Publisher { private string magazineName; public Publisher(string magazineName) { this.magazineName = magazineName; } public event EventHandler Magazine; public void PublishMagezine() { Magazine?.Invoke(this,this.magazineName); } } ``` 执行上述代码和之前的效果一样,其他自定义类应该也是可以的。 ### 4、总结 什么时候使用委托?什么时候使用事件? 如果一个委托不需要再其定义的类之外被触发,那么就可以将其转化为事件,这样可以保证它不会在外部被随意触发。