在本章中,主要是借机这个C#基础篇的系列整理过去的学习笔记、概括总结并更加理解透彻。html
在上一篇文章,咱们已经对委托有了进一步了解,委托至关于用方法做为另外一方法参数,同时,也能够实如今两个不能直接调用的方法中作桥梁。c#
下面咱们来回顾一下委托的例子。iphone
public delegate void ExecutingDelegate(string name); public class ExecutingManager { public void ExecuteProgram(string name, ExecutingDelegate ToExecuting) { ToExecuting(name); } } private static void StartExecute(string name) { Console.WriteLine("开始执行:" + name); } private static void EndExecute(string name) { Console.WriteLine("结束执行:" + name); } static void Main(string[] args) { ExecutingManager exec = new ExecutingManager(); exec.ExecuteProgram("开始。。。", StartExecute); exec.ExecuteProgram("结束。。。", EndExecute); Console.ReadKey(); }
根据上述的示例,再利用上节学到的知识,将多个方法绑定到同一个委托变量实现多播,该如何作呢?ide
再次修改代码:学习
static void Main(string[] args) { ExecutingManager exec = new ExecutingManager(); ExecutingDelegate executingDelegate; executingDelegate = StartExecute; executingDelegate += EndExecute; exec.ExecuteProgram("yuan", executingDelegate); Console.ReadKey(); }
可是,此刻咱们发现是否是能够将实例化声明委托的变量封装到ExecutingManager类中,这样是否是更加方便调用呢?ui
public class ExecutingManager { /// <summary> /// 在 ExecutingManager 类的内部声明 executingDelegate 变量 /// </summary> public ExecutingDelegate executingDelegate; public void ExecuteProgram(string name, ExecutingDelegate ToExecuting) { ToExecuting(name); } }
static void Main(string[] args) { ExecutingManager exec = new ExecutingManager(); exec.executingDelegate = StartExecute; exec.executingDelegate += EndExecute; exec.ExecuteProgram("yuan", exec.executingDelegate); Console.ReadKey(); }
写到这里了,这样作没有任何问题,但咱们发现这条语句很奇怪。在调用exec.ExecuteProgram方法的时候,再次传递了exec的executingDelegate字段, 既然如此,咱们何不修改 ExecutingManager类成这样:this
public class ExecutingManager { /// <summary> /// 在 GreetingManager 类的内部声明 delegate1 变量 /// </summary> public ExecutingDelegate executingDelegate; public void ExecuteProgram(string name) { if (executingDelegate != null) // 若是有方法注册委托变量 { executingDelegate(name); // 经过委托调用方法 } } }
static void Main(string[] args) { ExecutingManager exec = new ExecutingManager(); exec.executingDelegate = StartExecute; exec.executingDelegate += EndExecute; exec.ExecuteProgram("yuan"); Console.ReadKey(); }
这样再看,发现调用一下就更加简洁了。spa
在平常生活中,咱们可能都会遇到这样的各类各样的事情,而对于这些事情咱们都会采起相应的措施。好比,当你要给一个女神过生日的时候,你就能够给她送礼物。而这种状况,在C#开发中,就至关于过生日被看成事件来对待,而送礼物就是事件作出的响应。code
当女神过生日的时候,女神就会发布生日事件,而你就会接受到这个事件的通知,并作出响应的处理(送礼物等骚操做)。其中,触发这个事件的对象咱们可称之为事件发布者,而捕获这个事件并作出相应处理的称之为事件订阅者,咱们能够看出,女神就是充当了发布者,而你本身则充当了订阅者。htm
这里由生日事件引伸出两类角色,即事件发布者和事件订阅者。
在开发中,咱们是否遇到这样的情景,当一个特定的程序事件发生时,其余程序部分能够获得该事件注册发生通知。
发布者定义一系列事件,并提供一个注册方法;订阅者向发布者注册,并提供一个可被回调的方法,也就是事件处理程序;当事件被触发的时候,订阅者获得通知,而订阅者所提交的全部方法会被执行。
/// <summary> /// 先自定义一个委托 /// </summary> /// <param name="oldPrice"></param> /// <param name="newPrice"></param> public delegate void PriceChangedHandler(decimal oldPrice, decimal newPrice); /// <summary> /// 这个一个发布者 /// </summary> public class IPhone { decimal price; /// <summary> /// 定义一个事件 /// event 用来定义事件 /// PriceChangedHandler委托类型,事件须要经过委托来调用订阅者须要的方法 /// </summary> public event PriceChangedHandler PriceChanged; public decimal Price { get { return price; } set { if (price == value) return; decimal oldPrice = price; price = value; // 若是调用列表不为空,则触发。 if (PriceChanged != null) //用来判断事件是否被订阅者注册过 PriceChanged(oldPrice, price); //调用事件 } } } /// <summary> /// 这个一个订阅者 /// </summary> /// <param name="oldPrice"></param> /// <param name="price"></param> static void iPhone_PriceChanged(decimal oldPrice, decimal price) { Console.WriteLine("618促销活动,全场手机 只卖 " + price + " 元, 原价 " + oldPrice + " 元,快来抢!"); } static void Main() { ///实例化一个发布者类 IPhone phone = new IPhone() { Price = 5288 }; // 订阅事件 phone.PriceChanged += iPhone_PriceChanged; //完成事件的注册 调整价格(事件发生) phone.Price = 3999; //激发事件,并调用事件 Console.ReadKey(); }
输出:
618促销活动,全场手机 只卖 3999 元, 原价 5288 元,快来抢!
事件的声明语法:
//声明一个事件 public [static] event EventHandler EventName; //声明多个同类型的事件 public [static] event EventHandler EventName1, EventName2, EventName3;
事件必须声明在类或结构中,由于事件它不是一个类型,它是一个类或者结构中的一员。
在事件被触发以前,能够经过和null作比较,判断是否包含事件注册处理程序。由于事件成员被初始化默认是null。
委托类型EventHandler是声明专门用来事件的委托。事件提供了对委托的结构化访问;也便是没法直接访问事件中的委托。
查看源码:
事件的标准模式就是System命名空间下声明的EventHandler委托类型。
EventArgs是System下的一个类,以下:
using System.Runtime.InteropServices; namespace System { [Serializable] [ComVisible(true)] [__DynamicallyInvokable] public class EventArgs { [__DynamicallyInvokable] public static readonly EventArgs Empty = new EventArgs(); [__DynamicallyInvokable] public EventArgs() { } } }
根据EventArgs源码看出,EventArgs自己没法保存和传递数据的。
若是想保存和传递数据,能够实现一个EventArgs的派生类,而后定义相关的字段来保存和传递参数。
public class IPhone { decimal price; /// <summary> /// 使用EventHandler定义一个事件 /// </summary> public event EventHandler PriceChanged; protected virtual void OnPriceChanged() { if (PriceChanged != null) PriceChanged(this, null); } public decimal Price { get { return price; } set { if (price == value) return; decimal oldPrice = price; price = value; // 若是调用列表不为空,则触发。 if (PriceChanged != null) // //用来判断事件是否被订阅者注册过 OnPriceChanged(); } } } /// <summary> /// 这个一个订阅者 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> static void iphone_PriceChanged(object sender, EventArgs e) { Console.WriteLine("年终大促销,快来抢!"); } static void Main() { IPhone phone = new IPhone() { Price = 5288M }; // 订阅事件 phone.PriceChanged += iphone_PriceChanged; // 调整价格(事件发生) phone.Price = 3999; Console.ReadKey(); }
经过扩展EventHanlder来传递数据
System下另有泛型EventHandler类。由此,这里咱们能够将派生于EventArgs的类做为类型参数传递过来,这样,既能够得到派生类保存的数据。
///扩展类 public class PriceChangedEventArgs : System.EventArgs { public readonly decimal OldPrice; public readonly decimal NewPrice; public PriceChangedEventArgs(decimal oldPrice, decimal newPrice) { OldPrice = oldPrice; NewPrice = newPrice; } } public class IPhone { decimal price; public event EventHandler<PriceChangedEventArgs> PriceChanged; protected virtual void OnPriceChanged(PriceChangedEventArgs e) { if (PriceChanged != null) PriceChanged(this, e); } public decimal Price { get { return price; } set { if (price == value) return; decimal oldPrice = price; price = value; // 若是调用列表不为空,则触发。 if (PriceChanged != null) OnPriceChanged(new PriceChangedEventArgs(oldPrice, price)); } } } static void iphone_PriceChanged(object sender, PriceChangedEventArgs e) { Console.WriteLine("618促销活动,全场手机 只卖 " + e.NewPrice + " 元, 原价 " + e.OldPrice + " 元,快来抢!"); } static void Main() { IPhone phone = new IPhone() { Price = 5288M }; // 订阅事件 phone.PriceChanged += iphone_PriceChanged; // 调整价格(事件发生) phone.Price = 3999; Console.ReadKey(); }
输出
618促销活动,全场手机 只卖 3999 元, 原价 5288 元,快来抢!
能够利用 -= 运算符处理程序从事件中移除,当程序处理完后,能够将事件从中把它移除掉。
class Publiser { public event EventHandler SimpleEvent; public void RaiseTheEvent() { SimpleEvent(this, null); } } class Subscriber { public void MethodA(object o, EventArgs e) { Console.WriteLine("A"); } public void MethodB(object o, EventArgs e) { Console.WriteLine("B"); } } static void Main(string[] args) { Publiser p = new Publiser(); Subscriber s = new Subscriber(); p.SimpleEvent += s.MethodA; p.SimpleEvent += s.MethodB; p.RaiseTheEvent(); Console.WriteLine("\n移除B事件处理程序"); p.SimpleEvent -= s.MethodB; p.RaiseTheEvent(); Console.ReadKey(); }
输出:
运算符+= 、-=事件容许的惟一运算符。这些运算符是有预约义的行为。然而,咱们能够修改这些运算符的行为,让事件执行任何咱们但愿定义的代码。
能够经过为事件定义事件访问器,来控制事件运算符+=、-=运算符的行为
下面示例演示了具备访问器的声明.两个访问器都有叫作value的隐式值参数,它接受实例或静态方法的引用
public event EventHandler Elapsed { add { //... 执行+=运算符的代码 } remove { //... 执行-=运算符的代码 } }
声明了事件访问器后,事件不包含任何内嵌委托对象.咱们必须实现本身的机制来存储和移除事件的方法。
事件访问器表现为void方法,也就是不能使用会返回值的return语句。
示例:
//声明一个delegate delegate void EventHandler(); class MyClass { //声明一个成员变量来保存事件句柄(事件被激发时被调用的delegate) private EventHandler m_Handler = null; //激发事件 public void FireAEvent() { if (m_Handler != null) { m_Handler(); } } //声明事件 public event EventHandler AEvent { //添加访问器 add { //注意,访问器中实际包含了一个名为value的隐含参数 //该参数的值即为客户程序调用+=时传递过来的delegate Console.WriteLine("AEvent add被调用,value的HashCode为:" + value.GetHashCode()); if (value != null) { //设置m_Handler域保存新的handler m_Handler = value; } } //删除访问器 remove { Console.WriteLine("AEvent remove被调用,value的HashCode为:" + value.GetHashCode()); if (value == m_Handler) { //设置m_Handler为null,该事件将再也不被激发 m_Handler = null; } } } } static void Main(string[] args) { MyClass obj = new MyClass(); //建立委托 EventHandler MyHandler = new EventHandler(MyEventHandler); MyHandler += MyEventHandle2; //将委托注册到事件 obj.AEvent += MyHandler; //激发事件 obj.FireAEvent(); //将委托从事件中撤销 obj.AEvent -= MyHandler; //再次激发事件 obj.FireAEvent(); Console.ReadKey(); } //事件处理程序 static void MyEventHandler() { Console.WriteLine("This is a Event!"); } //事件处理程序 static void MyEventHandle2() { Console.WriteLine("This is a Event2!"); }
输出:
参考 文档 《C#图解教程》
注:搜索关注公众号【DotNet技术谷】--回复【C#图解】,可获取 C#图解教程文件