ref: http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx
http://blog.chinaunix.net/uid-576762-id-2733751.html
http://www.cnblogs.com/michaelxu/archive/2008/04/02/1134217.html
委托和事件总是放在一起,形成了消息响应机制是C#的核心机制,下面这段程序用委托和事件来对GreetingManager进行事件订阅。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace ConsoleTest 9 { 10 public delegate void GreetingDelegate(string name); 11 public class GreetingManager 12 { 13 public event GreetingDelegate MakeGreet; 14 public void GreetPeople(string name) 15 { 16 MakeGreet(name); 17 } 18 } 19 class Program 20 { 21 private static void EnglishGreeting(string name) 22 { 23 Console.WriteLine("Morning, " + name); 24 } 25 private static void ChineseGreeting(string name) 26 { 27 Console.WriteLine("早上好, " + name); 28 } 29 static void Main(string[] args) 30 { 31 //GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting); 32 //delegate1 += EnglishGreeting; 33 //delegate1 += ChineseGreeting; 34 //GreetPeople("Jimmy Zhang", EnglishGreeting); 35 //GreetPeople("张子阳", ChineseGreeting); 36 //delegate1("Jimmy Zhang"); 37 38 GreetingManager gm = new GreetingManager(); 39 //gm.GreetPeople("Jimmy Zhang", EnglishGreeting); 40 //gm.GreetPeople("张子阳", ChineseGreeting); 41 gm.MakeGreet += EnglishGreeting; 42 gm.MakeGreet += ChineseGreeting; 43 44 gm.GreetPeople("Jimmy Zhang"); 45 Console.ReadKey(); 46 } 47 } 48 }
上面的代码中EnglishGreeting和ChineseGreeting只是两个方法,实际情况可能是消息和事件响应分别来自不同的类,看下面的代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace ConsoleTest 9 { 10 public class Heater 11 { 12 private int temperature; 13 public delegate void BoilHandler(int param); 14 public event BoilHandler BoilEvent; 15 public void BoilWater() 16 { 17 for (int i = 0; i <= 100; i++) 18 { 19 temperature = i; 20 if (temperature > 95) 21 { 22 if (BoilEvent != null) BoilEvent(temperature); 23 } 24 } 25 } 26 } 27 public class Alarm 28 { 29 public void MakeAlert(int param) 30 { 31 Console.WriteLine("Alarm: dididi, water is {0} C.", param); 32 } 33 } 34 public class Display 35 { 36 public static void ShowMsg(int param) 37 { 38 Console.WriteLine("Display: water boiled, temperature is {0} C.", param); 39 } 40 } 41 class Program 42 { 43 static void Main(string[] args) 44 { 45 Heater heater = new Heater(); 46 Alarm alarm = new Alarm(); 47 48 heater.BoilEvent += alarm.MakeAlert; 49 heater.BoilEvent += (new Alarm()).MakeAlert; 50 heater.BoilEvent += Display.ShowMsg; 51 52 heater.BoilWater(); 53 Console.ReadKey(); 54 } 55 } 56 }
当然上面的代码并不符合.NET的规范标准,如下代码可以refer
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace ConsoleTest 9 { 10 public class BoiledEventArgs : EventArgs 11 { 12 private readonly int temperature; 13 public BoiledEventArgs(int temperature) 14 { 15 this.temperature = temperature; 16 } 17 public int Temperature 18 { 19 get { return temperature; } 20 } 21 } 22 public class Heater 23 { 24 private int temperature; 25 public string type = "RealFire 001"; 26 public string area = "China Xian"; 27 28 public delegate void BoiledEventHandler(object sender, BoiledEventArgs e); 29 public event BoiledEventHandler Boiled; 30 31 protected virtual void OnBoilded(Object sender, BoiledEventArgs e) 32 { 33 if (Boiled != null) Boiled(this, e); 34 } 35 36 public void BoilWater() 37 { 38 for (int i = 0; i <= 100; i++) 39 { 40 temperature = i; 41 if (temperature > 95) 42 { 43 BoiledEventArgs e = new BoiledEventArgs(temperature); 44 OnBoilded(this, e); 45 } 46 } 47 } 48 } 49 public class Alarm 50 { 51 public void OnBoiled(Object sender, BoiledEventArgs e) 52 { 53 Heater heater = (Heater)sender; 54 Console.WriteLine("Alarm: {0} - {1}", heater.area, heater.type); 55 Console.WriteLine("Alarm: dididi, water is {0} C.", e.Temperature); 56 Console.WriteLine(); 57 } 58 } 59 public class Display 60 { 61 public void OnBoiled(Object sender, BoiledEventArgs e) 62 { 63 Heater heater = (Heater)sender; 64 Console.WriteLine("Alarm: {0} - {1}", heater.area, heater.type); 65 Console.WriteLine("Display: water boiled, temperature is {0} C.", e.Temperature); 66 } 67 } 68 class Program 69 { 70 static void Main(string[] args) 71 { 72 Heater heater = new Heater(); 73 Alarm alarm = new Alarm(); 74 Display display = new Display(); 75 76 heater.Boiled += alarm.OnBoiled; 77 heater.Boiled += display.OnBoiled; 78 79 heater.BoilWater(); 80 Console.ReadKey(); 81 } 82 } 83 }
也可以用泛型委托的方式来处理,显得更加简单
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace ConsoleTest 9 { 10 public class BoiledEventArgs : EventArgs 11 { 12 private readonly int temperature; 13 public BoiledEventArgs(int temperature) 14 { 15 this.temperature = temperature; 16 } 17 public int Temperature 18 { 19 get { return temperature; } 20 } 21 } 22 public class Heater 23 { 24 private int temperature; 25 public string type = "RealFire 001"; 26 public string area = "China Xian"; 27 28 //public delegate void BoiledEventHandler(object sender, BoiledEventArgs e); 29 public event EventHandler<BoiledEventArgs> Boiled; 30 31 protected virtual void OnBoilded(Object sender, BoiledEventArgs e) 32 { 33 EventHandler<BoiledEventArgs> handler = Boiled; 34 if (handler != null) handler(sender, e); 35 } 36 37 public void BoilWater() 38 { 39 for (int i = 0; i <= 100; i++) 40 { 41 temperature = i; 42 if (temperature > 95) 43 { 44 BoiledEventArgs e = new BoiledEventArgs(temperature); 45 OnBoilded(this, e); 46 } 47 } 48 } 49 } 50 public class Alarm 51 { 52 public void OnBoiled(Object sender, BoiledEventArgs e) 53 { 54 Heater heater = (Heater)sender; 55 Console.WriteLine("Alarm: {0} - {1}", heater.area, heater.type); 56 Console.WriteLine("Alarm: dididi, water is {0} C.", e.Temperature); 57 Console.WriteLine(); 58 } 59 } 60 public class Display 61 { 62 public void OnBoiled(Object sender, BoiledEventArgs e) 63 { 64 Heater heater = (Heater)sender; 65 Console.WriteLine("Alarm: {0} - {1}", heater.area, heater.type); 66 Console.WriteLine("Display: water boiled, temperature is {0} C.", e.Temperature); 67 } 68 } 69 class Program 70 { 71 static void Main(string[] args) 72 { 73 Heater heater = new Heater(); 74 Alarm alarm = new Alarm(); 75 Display display = new Display(); 76 77 heater.Boiled += alarm.OnBoiled; 78 heater.Boiled += display.OnBoiled; 79 80 heater.BoilWater(); 81 Console.ReadKey(); 82 } 83 } 84 }
也可以将订阅放在类的构造函数中,这样主函数就显得简单了
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace ConsoleTest 9 { 10 class NewMailEventArgs : EventArgs 11 { 12 private readonly string m_from; 13 private readonly string m_to; 14 private readonly string m_subject; 15 public NewMailEventArgs(string from, string to, string subject) 16 { 17 m_from = from; 18 m_to = to; 19 m_subject = subject; 20 } 21 public string From 22 { 23 get { return m_from; } 24 } 25 public string To 26 { 27 get { return m_to; } 28 } 29 public string Subject 30 { 31 get { return m_subject; } 32 } 33 } 34 35 class MailManager 36 { 37 public delegate void NewMailEventHandler(object sender, NewMailEventArgs e); 38 public event NewMailEventHandler NewMail; 39 protected virtual void OnNewMail(Object sender, NewMailEventArgs e) 40 { 41 if (NewMail != null) NewMail(this, e); 42 } 43 public void SimulateNewMail(string from, string to, string subject) 44 { 45 NewMailEventArgs e = new NewMailEventArgs(from, to, subject); 46 OnNewMail(this, e); 47 } 48 } 49 class Fax 50 { 51 public Fax(MailManager mm) 52 { 53 mm.NewMail += Fax_NewMail; 54 } 55 private void Fax_NewMail(object sender, NewMailEventArgs e) 56 { 57 Console.WriteLine("Message arrived at Fax..."); 58 Console.WriteLine("From {0}, To = {1}, Subject = '{2}'", e.From, e.To, e.Subject); 59 } 60 public void Unregister(MailManager mm) 61 { 62 mm.NewMail -= Fax_NewMail; 63 } 64 } 65 class Print 66 { 67 public Print(MailManager mm) 68 { 69 mm.NewMail += Print_NewMail; 70 } 71 private void Print_NewMail(object sender, NewMailEventArgs e) 72 { 73 Console.WriteLine("Message arrived at Print..."); 74 Console.WriteLine("From = {0}, To = {1}, Subject = '{2}'", e.From, e.To, e.Subject); 75 } 76 public void Unregister(MailManager mm) 77 { 78 mm.NewMail -= Print_NewMail; 79 } 80 } 81 82 class Program 83 { 84 [STAThread] 85 static void Main(string[] args) 86 { 87 MailManager mm = new MailManager(); 88 Fax fax = new Fax(mm); 89 Print prt = new Print(mm); 90 mm.SimulateNewMail("Anco", "Jerry", "Event test"); 91 Console.ReadKey(); 92 } 93 } 94 }
下面的monitor的run函数是一个阻塞型函数
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 8 namespace ConsoleTest 9 { 10 class KeyEventArgs : EventArgs 11 { 12 private readonly char keyChar; 13 public KeyEventArgs(char keyChar) 14 { 15 this.keyChar = keyChar; 16 } 17 public char KeyChar 18 { 19 get { return keyChar; } 20 } 21 } 22 23 class KeyInputMonitor 24 { 25 public delegate void KeyDownEventHandler(object sender, KeyEventArgs e); 26 public event KeyDownEventHandler KeyDown; 27 protected virtual void OnKeyDown(Object sender, KeyEventArgs e) 28 { 29 if (KeyDown != null) KeyDown(this, e); 30 } 31 public void Run() 32 { 33 bool finished = false; 34 do { 35 Console.WriteLine("Input a char ..."); 36 string response = Console.ReadLine(); 37 char responseChar = response == ""? ' ': char.ToUpper(response[0]); 38 switch(responseChar) 39 { 40 case 'X': 41 finished = true; 42 break; 43 default: 44 OnKeyDown(this, new KeyEventArgs(responseChar)); 45 break; 46 } 47 }while(!finished); 48 } 49 } 50 class EventReceiver 51 { 52 public EventReceiver(KeyInputMonitor monitor) 53 { 54 monitor.KeyDown += this.OnKeyDown; 55 } 56 private void OnKeyDown(object sender, KeyEventArgs e) 57 { 58 Console.WriteLine("Capture key: {0}", e.KeyChar); 59 } 60 } 61 62 class Program 63 { 64 [STAThread] 65 static void Main(string[] args) 66 { 67 KeyInputMonitor monitor = new KeyInputMonitor(); 68 EventReceiver eventReceiver = new EventReceiver(monitor); 69 monitor.Run(); 70 } 71 } 72 }
http://wenku.baidu.com/link?url=6mbGCmtn9W6vLQndZFqPcqv-8qFOrEcJtfijpUHv-YUjv_Fs4pNtveRuTED4Gws93ftXGueAnOfqTOYaBfc667jT41fGALMwIoQWW3B3j8C
还是回到heater那个例子,如果需要对响应函数加入异常处理,这里如果没有用委托链表的话,前面的异常会影响到后面委托函数的运行,因此需要加入委托链表。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 using System.Runtime.Remoting.Messaging; 8 9 namespace ConsoleTest 10 { 11 public class BoiledEventArgs : EventArgs 12 { 13 private readonly int temperature; 14 public BoiledEventArgs(int temperature) 15 { 16 this.temperature = temperature; 17 } 18 public int Temperature 19 { 20 get { return temperature; } 21 } 22 } 23 public class Heater 24 { 25 private int temperature; 26 public string type = "RealFire 001"; 27 public string area = "China Xian"; 28 29 public delegate int BoiledEventHandler(object sender, BoiledEventArgs e); 30 public event BoiledEventHandler Boiled; 31 32 protected virtual void OnBoilded(Object sender, BoiledEventArgs e) 33 { 34 if (Boiled != null) { 35 Delegate[] delArray = Boiled.GetInvocationList(); 36 foreach(Delegate del in delArray) 37 { 38 BoiledEventHandler method = (BoiledEventHandler)del; 39 try 40 { 41 //string data = "Both Complete!!"; 42 //IAsyncResult asyncResult = method.BeginInvoke(this, e, MSG.Complete, data); 43 //method.BeginInvoke(this, e, null, null); 44 method(this, e); 45 //int rtn = method.EndInvoke(asyncResult); 46 //int rtn = getReturn(asyncResult); 47 //throw new Exception(); 48 } 49 catch (Exception ex) 50 { 51 Console.WriteLine("Excetion: {0}", ex.Message); 52 } 53 54 } 55 Console.WriteLine("Message Thread Completed"); 56 } 57 } 58 59 /*private static int getReturn(IAsyncResult asyncResult) 60 { 61 AsyncResult result = (AsyncResult)asyncResult; 62 BoiledEventHandler method = (BoiledEventHandler)result.AsyncDelegate; 63 int rtn = method.EndInvoke(asyncResult); 64 return rtn; 65 }*/ 66 67 public void BoilWater() 68 { 69 for (int i = 0; i <= 100; i++) 70 { 71 temperature = i; 72 if (temperature > 95) 73 { 74 BoiledEventArgs e = new BoiledEventArgs(temperature); 75 OnBoilded(this, e); 76 } 77 } 78 } 79 } 80 public class Alarm 81 { 82 public int OnBoiled(Object sender, BoiledEventArgs e) 83 { 84 Heater heater = (Heater)sender; 85 Thread.Sleep(1000); 86 Console.WriteLine("Alarm: {0} - {1}", heater.area, heater.type); 87 Console.WriteLine("Alarm: dididi, water is {0} C.", e.Temperature); 88 Console.WriteLine(); 89 throw new Exception(); 90 return 0; 91 } 92 } 93 public class Display 94 { 95 public int OnBoiled(Object sender, BoiledEventArgs e) 96 { 97 Heater heater = (Heater)sender; 98 Thread.Sleep(1000); 99 Console.WriteLine("Display: {0} - {1}", heater.area, heater.type); 100 Console.WriteLine("Display: water boiled, temperature is {0} C.", e.Temperature); 101 Console.WriteLine(); 102 return 0; 103 } 104 } 105 /*public class MSG 106 { 107 public static void Complete(IAsyncResult asynResult) 108 { 109 AsyncResult result = (AsyncResult)asynResult; 110 Heater.BoiledEventHandler method = (Heater.BoiledEventHandler)result.AsyncDelegate; 111 string data = (string)asynResult.AsyncState; 112 int rtn = method.EndInvoke(asynResult); 113 Console.WriteLine("Result: {0}, Data: {1}", rtn, data); 114 } 115 }*/ 116 class Program 117 { 118 static void Main(string[] args) 119 { 120 Heater heater = new Heater(); 121 Alarm alarm = new Alarm(); 122 Display display = new Display(); 123 124 heater.Boiled += alarm.OnBoiled; 125 heater.Boiled += display.OnBoiled; 126 127 heater.BoilWater(); 128 Console.ReadKey(); 129 } 130 } 131 }
如果display和alarm有执行的时间,可以用Thread.Sleep()来模拟,这个时候主线程就会等响应函数完成之后再继续执行下面的程序,如果消息响应函数执行时间过长,就会影响系统性能,可以用委托异步技术来解决这个问题。
委托有三个函数:Invoke, BeginInvoke和EndInvoke. 直接调用委托是用invoke这个函数,这里我们用BeginInvoke这个函数来从线程池抓取闲置线程,客户端可以继续执行下一个响应函数。BeginInvoke的参数是在编译时根据委托定义动态产生的,最后两个参数是AsyncCallback和Object类型,这里先用null来表示,BeginInvoke并不会抛出异常,因此try…catch模块要放入响应函数中
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.Threading; 7 using System.Runtime.Remoting.Messaging; 8 9 namespace ConsoleTest 10 { 11 public class BoiledEventArgs : EventArgs 12 { 13 private readonly int temperature; 14 public BoiledEventArgs(int temperature) 15 { 16 this.temperature = temperature; 17 } 18 public int Temperature 19 { 20 get { return temperature; } 21 } 22 } 23 public class Heater 24 { 25 private int temperature; 26 public string type = "RealFire 001"; 27 public string area = "China Xian"; 28 29 public delegate int BoiledEventHandler(object sender, BoiledEventArgs e); 30 public event BoiledEventHandler Boiled; 31 32 protected virtual void OnBoilded(Object sender, BoiledEventArgs e) 33 { 34 if (Boiled != null) { 35 Delegate[] delArray = Boiled.GetInvocationList(); 36 foreach(Delegate del in delArray) 37 { 38 BoiledEventHandler method = (BoiledEventHandler)del; 39 IAsyncResult asyncResult = method.BeginInvoke(this, e, null, null); 40 } 41 Console.WriteLine("Message Thread Completed"); 42 Console.WriteLine(); 43 } 44 } 45 46 public void BoilWater() 47 { 48 for (int i = 0; i <= 100; i++) 49 { 50 temperature = i; 51 if (temperature > 95) 52 { 53 BoiledEventArgs e = new BoiledEventArgs(temperature); 54 OnBoilded(this, e); 55 } 56 } 57 } 58 } 59 public class Alarm 60 { 61 public int OnBoiled(Object sender, BoiledEventArgs e) 62 { 63 try 64 { 65 Heater heater = (Heater)sender; 66 Thread.Sleep(1000); 67 Console.WriteLine("Alarm: {0} - {1}", heater.area, heater.type); 68 Console.WriteLine("Alarm: dididi, water is {0} C.", e.Temperature); 69 Console.WriteLine(); 70 throw new Exception(); 71 } 72 catch (Exception ex) 73 { 74 Console.WriteLine("Excetion: {0}", ex.Message); 75 Console.WriteLine(); 76 } 77 return 0; 78 } 79 } 80 public class Display 81 { 82 public int OnBoiled(Object sender, BoiledEventArgs e) 83 { 84 try 85 { 86 Heater heater = (Heater)sender; 87 Thread.Sleep(1000); 88 Console.WriteLine("Display: {0} - {1}", heater.area, heater.type); 89 Console.WriteLine("Display: water boiled, temperature is {0} C.", e.Temperature); 90 Console.WriteLine(); 91 } 92 catch (Exception ex) 93 { 94 Console.WriteLine("Excetion: {0}", ex.Message); 95 Console.WriteLine(); 96 } 97 return 0; 98 } 99 } 100 101 class Program 102 { 103 static void Main(string[] args) 104 { 105 Heater heater = new Heater(); 106 Alarm alarm = new Alarm(); 107 Display display = new Display(); 108 109 heater.Boiled += alarm.OnBoiled; 110 heater.Boiled += display.OnBoiled; 111 112 heater.BoilWater(); 113 Console.ReadKey(); 114 } 115 } 116 }
至于EndInvoke和后面两个参数的应用我想不出有什么案例可以用,可以ref上面的reference