【C#】事件(Event)和代理/委托(Delegate)
代理(Delegate)的例子
delegate void MyDelegate(string str,int index); // 声明代理 class Test { public static void Show(string str, int index) // 声明方法 { Console.WriteLine("Show"+str+index.ToString()); } public static void Main(string[] args) { MyDelegate md = new MyDelegate(Show); // 1.实例化代理,传入方法 md("hello world", 22); // 2.传入参数 } }
事件结合代理的完整例子
// 事件用到的代理,以般以×××Handler的格式进行命名 private delegate void CryHandler(); // 无参代理 // 玩具小鸭的类 class Duck { // 定义小鸭的唱歌事件 public event CryHandler DuckCryEvent; public Duck() { // 把小鸭唱歌的事件挂接到Cry方法上 DuckCryEvent += new CryHandler(Cry); // 注册事件,传入方法 } // 小鸭唱歌事件对应的处理方法 public void Cry() { Console.WriteLine("我是一只小鸭,呀呀呀...."); } // 小鸭被摇动 public void BeShaked() //执行方法,引发cry事件 { DuckCryEvent(); // 执行事件,传入参数 } } class MyClass { public static void Main3(string[] args) { // 买一只小鸭 Duck d = new Duck(); // 摇一摇小鸭,它就会调触发小鸭的Cry事件,小鸭就会唱歌 d.BeShaked(); } }
参考:
事件与委托的区别?
实际上,事件是对委托的封装。如果不进行封装,让委托暴露给调用者,调用者就可以把委托变量重新引用到新的委托对象,也就删除了当前要调用的方法列表;更糟糕的是,公共的委托成员打破了封装不仅导致代码难以维护和调试,而且会导致应用程序有安全风险。
参考:
- https://stackoverflow.com/questions/29155/what-are-the-differences-between-delegates-and-events
- http://blog.csdn.net/hit_why/article/details/54604908
- https://www.cnblogs.com/wujingtao/p/5196834.html
怎么理解事件对函数列表的保护作用呢?看下面的例子。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { class Test { public delegate void MyDelegate(string str, int index); // 代理可以声明在类的外部 public event MyDelegate MyHandler; // 事件必须在类内部定义 public void FireEvent() { MyHandler = Show; // 事件的函数列表交由定义方维护,不会被调用者修改! MyHandler += Show; MyHandler("test", 1); // 会调用两次Show() } public void Show(string str, int index) { Console.WriteLine("Show : " + str + index.ToString()); } } class Program { static void Main(string[] args) { // 使用代理 Test t = new Test(); Test.MyDelegate my = new Test.MyDelegate(t.Show); // 修改参数,调用方甚至可以修改代理的函数列表 my += DoSomething; // 调用方可自由修改被调用方定义的委托的函数列表! //my.Invoke("这是Program.DoSomething()", 2); // 运行结果:先调用t.Show()再调用DoSomething() // 使用事件 t.MyHandler += DoSomething; // t.MyHandler("12", 1); // 报错:Test.MyHandler只能出现在 += 或 -= 的左边(从类型“Test”中使用时除外) // t.MyHandler.Invoke("这是Program.DoSomething()", 2); // 错误,事件不存在Invoke()调用,只会被调用方触发,再由事件定义方自行来调用 t.FireEvent(); // 运行结果:会发现DoSomething()没有被调用,因为事件中的函数列表由被调用方维护,不会被调用方修改! Console.ReadLine(); } public static void DoSomething(string str, int index) { Console.WriteLine("DoSomething"); } } }
可见,如果使用delegate代理,代理的函数列表会暴露给调用者,调用者可以任意修改它(只要符合代理声明的方法传参、返回值即可)!这样被调用者将无法约束调用者的行为。
而如果使用evnet事件,由于调用者只能触发事件,事件只能由被调用方来调用,所以即便调用者尝试修改事件的函数列表,但真到调用时也无法改变事件的函数列表。即函数列表始终由被调用者来维护,调用者的行为被约束了!