C#: 委托和事件

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 }
View Code

 上面的代码中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 }
View Code

 当然上面的代码并不符合.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 }
View Code

 也可以用泛型委托的方式来处理,显得更加简单

 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 }
View Code

 

 也可以将订阅放在类的构造函数中,这样主函数就显得简单了

 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 }
View Code

 下面的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 }
View Code

 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 }
View Code

 

如果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 }
View Code

至于EndInvoke和后面两个参数的应用我想不出有什么案例可以用,可以ref上面的reference

 

你可能感兴趣的:(C#)