一、委托和事件
委托和事件这两个概念是完全配合的。委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成。委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数。事件则是委托的一种表现形式。
委托的声明:[修饰符] delegate 返回类型 委托名(参数列表);
简单的委托
using System; using System; using System.Collections.Generic; using System.Text; namespace TestApp { /// <summary> /// 委托 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public delegate string ProcessDelegate(string s1, string s2); class Program { static void Main(string[] args) { /* 调用方法 */ ProcessDelegate pd = new ProcessDelegate(new Test().Process); Console.WriteLine(pd("Text1", "Text2")); } } public class Test { /// <summary> /// 方法 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public string Process(string s1, string s2) { return s1 + s2; } } }
泛型委托
using System; using System.Collections.Generic; using System.Text; namespace TestApp { /// <summary> /// 委托 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public delegate string ProcessDelegate<T,S>(T s1, S s2); class Program { static void Main(string[] args) { /* 调用方法 */ ProcessDelegate<string,int> pd = new ProcessDelegate<string,int>(new Test().Process); Console.WriteLine(pd("Text1", 100)); } } public class Test { /// <summary> /// 方法 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public string Process(string s1,int s2) { return s1 + s2; } } }
委托的回调方法
using System; using System.Collections.Generic; using System.Text; namespace TestApp { /// <summary> /// 委托 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public delegate string ProcessDelegate(string s1, string s2); class Program { static void Main(string[] args) { /* 调用方法 */ Test t = new Test(); string r1 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process1)); string r2 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process2)); string r3 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process3)); Console.WriteLine(r1); Console.WriteLine(r2); Console.WriteLine(r3); } } public class Test { public string Process(string s1,string s2,ProcessDelegate process) { return process(s1, s2); } public string Process1(string s1, string s2) { return s1 + s2; } public string Process2(string s1, string s2) { return s1 + Environment.NewLine + s2; } public string Process3(string s1, string s2) { return s2 + s1; } } }
事件的声明:[修饰符] event 委托名 事件名;
事件应该由事件发布者触发,而不应该由客户端(客户程序)来触发。注意这里术语的变化,当我们单独谈论事件,我们说发布者(publisher)、订阅者(subscriber)、客户端(client)。当我们讨论Observer模式,我们说主题(subject)和观察者(observer)。客户端通常是包含Main()方法的Program类。
1.为什么要使用事件而不是委托变量? (摘自http://kb.cnblogs.com/page/45756/)
using System; using System.Collections.Generic; using System.Text; // 实例:为什么使用事件而不是委托变量 namespace ConsoleApp { class Program { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber sub = new Subscriber(); pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged); pub.DoSomething(); // 应该这样触发事件 pub.NumberChanged("使用委托"); // 但是被这样调用了,对委托变量的恰当使用 } } // 定义委托 public delegate void NumberChangedEventHandler(string Name); // 定义事件发布者 public class Publishser { private string Name; public NumberChangedEventHandler NumberChanged; // 声明委托变量 //public event NumberChangedEventHandler NumberChanged; // 声明一个事件 public void DoSomething() { // 在这里完成一些工作 ... if (NumberChanged != null) { // 触发事件 NumberChanged(Name); } } } // 定义事件订阅者 public class Subscriber { public void OnNumberChanged(string Name) { Console.WriteLine("{0}已响应", Name); } } }
2.如何让事件只允许一个客户订阅?(事件访问器)
using System; using System.Collections.Generic; using System.Text; // 实例:让事件只允许一个客户订阅 namespace ConsoleApp { class Program3 { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); pub.NumberChanged -= sub1.OnNumberChanged; // 不会有任何反应 pub.NumberChanged += sub2.OnNumberChanged; // 注册了sub2 pub.NumberChanged += sub1.OnNumberChanged; // sub1将sub2的覆盖掉了 pub.DoSomething(); // 触发事件 } } // 定义委托 public delegate string GeneralEventHandler(); // 定义事件发布者 public class Publishser { // 声明一个委托变量或事件都无所谓 private GeneralEventHandler numberChanged; // 事件访问器的定义 public event GeneralEventHandler NumberChanged { add { numberChanged = value; //只允许注册一个事件,重复注册则替换前者 } remove { numberChanged -= value; } } public void DoSomething() { // 做某些其他的事情 if (numberChanged != null) { // 触发事件 string rtn = numberChanged(); Console.WriteLine("Return: {0}", rtn); // 打印返回的字符串 } } } // 定义事件订阅者 public class Subscriber1 { public string OnNumberChanged() { Console.WriteLine("Subscriber1 Invoked!"); return "Subscriber1"; } } public class Subscriber2 { public string OnNumberChanged() { Console.WriteLine("Subscriber2 Invoked!"); return "Subscriber2"; } } }
3.处理异常和订阅者方法超时的处理
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Runtime.Remoting.Messaging; using System.IO; // 处理异常 namespace ConsoleApp { class Program6 { static void Main(string[] args) { Publisher pub = new Publisher(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.MyEvent += new EventHandler(sub1.OnEvent); pub.MyEvent += new EventHandler(sub2.OnEvent); pub.MyEvent += new EventHandler(sub3.OnEvent); pub.DoSomething(); // 触发事件 Console.WriteLine("Control back to client! "); // 返回控制权 Console.WriteLine("Press any thing to exit... "); Console.ReadKey(); // 暂停客户程序,提供时间供订阅者完成方法 } } public class Publisher { public event EventHandler MyEvent; public void DoSomething() { // 做某些其他的事情 Console.WriteLine("DoSomething invoked!"); if (MyEvent != null) { Delegate[] delArray = MyEvent.GetInvocationList(); foreach (Delegate del in delArray) { EventHandler method = (EventHandler)del; method.BeginInvoke(null, EventArgs.Empty, null, null); } } } } public class Subscriber1 { public void OnEvent(object sender, EventArgs e) { Thread.Sleep(TimeSpan.FromSeconds(3)); // 模拟耗时三秒才能完成方法 Console.WriteLine("Waited for 3 seconds, subscriber1 invoked!"); } } public class Subscriber2 { public void OnEvent(object sender, EventArgs e) { throw new Exception("Subsciber2 Failed"); // 即使抛出异常也不会影响到客户端 //Console.WriteLine("Subscriber2 immediately Invoked!"); } } public class Subscriber3 { public void OnEvent(object sender, EventArgs e) { Thread.Sleep(TimeSpan.FromSeconds(2)); // 模拟耗时两秒才能完成方法 Console.WriteLine("Waited for 2 seconds, subscriber3 invoked!"); } } }
4.委托和方法的异步调用
using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Runtime.Remoting.Messaging; namespace ConsoleApp { public delegate int AddDelegate(int x, int y); class Program9 { static void Main(string[] args) { Console.WriteLine("--------客户端执行开始,即将调用异步"); Thread.CurrentThread.Name = "客户端线程Name"; Calculator cal = new Calculator(); AddDelegate del = new AddDelegate(cal.Add); string data = "客户端数据."; //异步完成后挂载的方法返回的数据 AsyncCallback callBack = new AsyncCallback(OnAddComplete); //异步完成后挂载方法 del.BeginInvoke(2, 5, callBack, data); // 异步调用方法 // 做某些其它的事情,模拟需要执行3秒钟 for (int i = 1; i <= 3; i++) { Thread.Sleep(TimeSpan.FromSeconds(i)); Console.WriteLine("{0}: 模拟执行其他事情 {1} 秒钟.", Thread.CurrentThread.Name, i); } Console.WriteLine(" -------客户端执行完毕..."); Console.ReadKey(); } static void OnAddComplete(IAsyncResult asyncResult) { AsyncResult result = (AsyncResult)asyncResult; AddDelegate del = (AddDelegate)result.AsyncDelegate; string data = (string)asyncResult.AsyncState; int rtn = del.EndInvoke(asyncResult); Console.WriteLine("{0}: 异步返回值, {1}; Data: {2} ", Thread.CurrentThread.Name, rtn, data); } } public class Calculator { public int Add(int x, int y) { if (Thread.CurrentThread.IsThreadPoolThread) { Thread.CurrentThread.Name = "异步线程Name"; } Console.WriteLine("--------异步开始!"); // 执行某些事情,模拟需要执行2秒钟 for (int i = 1; i <= 2; i++) { Thread.Sleep(TimeSpan.FromSeconds(i)); Console.WriteLine("{0}: 模拟执行事情 {1} 秒钟.", Thread.CurrentThread.Name, i); } Console.WriteLine("--------异步结束!"); return x + y; } } }
二、异常和错误 (try-catch-finally)
基类:System.Exception
try语句提供了一种机制来捕捉块执行过程中发生的异常。以下是它的三种可能的形式(s可多个catch):
●try-catch(s)
●try-finally
●try-catch(s)-finally
try{申请资源,如数据库连接,网络连接,打开文件等可能出现异常的代码}
catch(异常类型 e){处理异常一类型的异常}
finally{释放资源}
手动跑出异常:throw new System.Exception();