本文是學習 博客C# 中的委託和事件(詳解) 的心得 ,博客原文爲:http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html
委託和事件的用法:
使用委託和事件的目的,在這個例子中,加熱器(heater)只負責加熱,而報警器(alamter)負責報警和顯示器(viewer)負責顯示,而加熱器如何告訴報警器和顯示器?這就需要用到委託(事件)。
使用到了觀察者模式
爲什麼委託定義的返回值通常都爲 void ?
儘管並非必需,但是我們發現很多的委託定義返回值都爲 void,爲什麼呢?這是因爲委託變量可以供多個訂閱者註冊,如果定義了返回值,那麼多個訂閱者的方法都會向發佈者返回數值,結果就是後面一個返回的方法值將前面的返回值覆蓋掉了,因此,實際上只能獲得最後一個方法調用的返回值。可以運行下面的代碼測試一下。除此以外,發佈者和訂閱者是鬆耦合的,發佈者根本不關心誰訂閱了它的事件、爲什麼要訂閱,更別說訂閱者的返回值了,所以返回訂閱者的方法返回值大多數情況下根本沒有必要
using System;
//編碼規範
//1. 委託類型的名稱都應該以 EventHandler 結束。
//2. 委託的原型定義:有一個void 返回值,
//並接受兩個輸入參數:一個Object 類型,一個EventArgs 類型(或繼承自EventArgs)。
//3. 事件的命名爲委託去掉 EventHandler 之後剩餘的部分。
//4. 繼承自 EventArgs 的類型應該以EventArgs 結尾。
namespace testEvent
{
class MainClass
{
public static void Main1 (string[] args)
{
Heater heater = new Heater ();
Alamter a = new Alamter ();
heater.sendTempure -= a.OnAlam;
heater.sendTempure += a.OnAlam;
heater.sendTempure += new Viewer ().OnView;
heater.boillWater ();
}
}
//監視對象,裏面含有被監視的內容:溫度
public class Heater{
public String Name {
get;
set;
}
public int Tempurea {
get;
set;
}
//定義成void,是爲了避免多個事件註冊而返回值被覆蓋,
//如果有這種需求,可以用特殊方法來避免被多次註冊。
//Object sender 用於監視者強轉成監視對象,獲取到監視對象的屬性,如Name。
//
public delegate void sendTempureEventHander (Object sender,BoilEventArgs e);
public event sendTempureEventHander sendTempure;
//這個地方必須是空的,因爲真正執行任務的是Alamter和Viewer這兩個監視者類,
//而監視對象只是用這個handler來佔位而已
//要真正使sendTempure內容不爲空,需要在真正使用時,綁定監視者的具體方法。
public void boillWater(){
for(int i=0;i<100;i++){
Console.WriteLine ("開始燒水");
if(i>90){
BoilEventArgs boilEventArgs = new BoilEventArgs (i);
sendTempure (this, boilEventArgs);
}
}
}
}
//承自 EventArgs 的類型應該以EventArgs 結尾。
public class BoilEventArgs:EventArgs {
public int Tempure {
get;
set;
}
public BoilEventArgs(int tempure){
this.Tempure = tempure;
}
}
//監視者
//用於報警的類
public class Alamter{
//這裏還有一個約定俗稱的規定,就是訂閱事件的方法的命名,通常爲“On 事件名”,比如這裏的OnNumberChanged
public void OnAlam(Object sender, BoilEventArgs e){
Console.WriteLine ("名字爲"+((Heater)sender).Name+"溫度達到了:"+e.Tempure+"警報器開始報警");
}
public static void OnAlamt(Object sender, BoilEventArgs e){
Console.WriteLine ("名字爲"+((Heater)sender).Name+"溫度達到了:"+e.Tempure+"警報器開始報警");
}
}
//用於顯示的類
public class Viewer{
public void OnView(Object sender, BoilEventArgs e){
Console.WriteLine ("名字爲"+((Heater)sender).Name+"溫度達到了:"+e.Tempure+"顯示器開始顯示");
}
}
}
如何讓事件只允許一個客戶訂閱?
using System;
namespace OneEvent
{
public class OneEventPusher{
public String Name{ get; set;}
public delegate void ChangeValueEventHandler(Object sender, ChangeEventArgs e);
private event ChangeValueEventHandler changeValue;
public void Register(ChangeValueEventHandler handler){
changeValue = handler;
}
//如果changValue爲空,-=操作也不會報錯
public void UnRegister(ChangeValueEventHandler handler){
changeValue -= handler;
}
public void doWork(int i){
//因爲在Register 方法時使用的是=,每次changeValue只會註冊一個方法,所以,可能會發生UnRegister時,changeValue爲空,故需要判斷一下
if(changeValue!=null){
ChangeEventArgs args = new ChangeEventArgs (i);
changeValue(this,args);
}
}
}
public class ChangeEventArgs:EventArgs{
public int passValue {
get;
set;
}
public ChangeEventArgs(int pass_value){
this.passValue = pass_value;
}
}
public class OneEventWorker1{
public void OnChange(Object sender,ChangeEventArgs changerArgs){
Console.WriteLine ("I am worker 1!");
Console.WriteLine (((OneEventPusher)sender).Name);
Console.WriteLine (changerArgs.passValue);
}
}
public class OneEventWorker2{
public void OnChange(Object sender,ChangeEventArgs changerArgs){
Console.WriteLine ("I am worker 2!");
Console.WriteLine (((OneEventPusher)sender).Name);
Console.WriteLine (changerArgs.passValue);
}
}
class MainClass
{
public static void Main2 (string[] args)
{
OneEventPusher one = new OneEventPusher ();
one.Name ="Test";
OneEventWorker1 work1 = new OneEventWorker1 ();
one.Register (work1.OnChange);
one.UnRegister (work1.OnChange);
//one.UnRegister (work2.OnChange);
one.doWork (1);
}
}
}
如何使用委託異步調用觀察者:
using System.Threading;
using System;
using System.Runtime.Remoting.Messaging;
namespace UnSynzDemo{
public delegate void Count();
class MainClass
{
//同步執行
public static void Main3 (string[] args)
{
Console.WriteLine ("Thread name is ---"+Thread.CurrentThread.Name);
Count count = new Count (new CountClass ().getCounts);
count() ;
Console.WriteLine ("main Class is over");
}
//異步執行
public static void Main (string[] args)
{
if (Thread.CurrentThread.IsThreadPoolThread) {
Thread.CurrentThread.Name = "this is ThreadPoolThread";
} else {
Thread.CurrentThread.Name = "this is mainThread";
}
Console.WriteLine ("Thread name is ---"+Thread.CurrentThread.Name);
Counter counter = new Counter ();
counter.doWork ();
Thread.Sleep (5000);
Console.WriteLine ("main Thread is over");
}
}
class CountClass{
public void getCounts(){
int count = 0;
if (Thread.CurrentThread.IsThreadPoolThread) {
Thread.CurrentThread.Name = "this is ThreadPoolThread";
} else {
Thread.CurrentThread.Name = "this is mainThread";
}
Console.WriteLine ("CountClass Thread Name is "+ Thread.CurrentThread.Name);
for(int i =0;i<10;i++){
count += i;
Thread.Sleep (TimeSpan.FromMilliseconds(300));
}
Console.WriteLine (count);
}
public int OnCounts(int c){
if (Thread.CurrentThread.IsThreadPoolThread) {
Thread.CurrentThread.Name = "this is ThreadPoolThread";
} else {
Thread.CurrentThread.Name = "this is mainThread";
}
Console.WriteLine ("CountClass Thread Name is "+ Thread.CurrentThread.Name);
int totolCount = 0;
for(int i =0;i<c;i++){
totolCount += i;
}
return totolCount;
}
}
//用於執行異步操作的delegate
public delegate int CountNumEventHandler(int c);
//系統自帶的delegate,用於綁定回調函數的方法,參數爲固定的IAsyncResult類型
//public delegate void AsyncCallback(IAsyncResult ar);
class Counter{
public void doWork(){
Console.WriteLine ("doWork Thread Name is "+ Thread.CurrentThread.Name);
//調用BeginInvoke前面的參數和delegate的參數一致,
//而後面兩個參數,第一個參數表示回調函數,第二個參數表示傳遞的參數(兩個參數都可以爲null值)
//BeginInvoke的返回值爲
int c = 10;
string data = "123";
CountNumEventHandler handler = new CountNumEventHandler (new CountClass().OnCounts);
AsyncCallback callBack = new AsyncCallback (CounterCallBack);
//後面兩個參數都可以爲null值
//handler.BeginInvoke (c,null,null);
//delegate 參數爲空的情況
//handler.BeginInvoke(null,null);
//可以直接獲取相應的運行結果和傳遞的參數
//IAsyncResult IResult = handler.BeginInvoke(c,callBack,data);
//IAsyncResult IResult = handler.BeginInvoke (c,null,data);
//AsyncResult result = (AsyncResult)IResult;
//int i = handler.EndInvoke (result);
//Console.WriteLine (result.AsyncState);
//Console.WriteLine (i);
//Console.WriteLine ("do Working is over");
//也可以在回調函數中獲取相應的運行結果和傳遞的參數
IAsyncResult IResult = handler.BeginInvoke(c,callBack,data);
}
//回調函數
public void CounterCallBack(IAsyncResult ar){
Console.WriteLine ("callBack Thread Name is "+ Thread.CurrentThread.Name);
//轉爲AsyncResult,此時就可以獲取相應的屬性了
AsyncResult result = (AsyncResult)ar;
CountNumEventHandler handler = (CountNumEventHandler)result.AsyncDelegate;
//獲取方法的返回值
int rtn = handler.EndInvoke(result);
Console.WriteLine ("CallBack return is "+rtn);
string data = (string)result.AsyncState;
Console.WriteLine ("CallBack passData is "+data);
Console.WriteLine (data);
Console.WriteLine ("CallBack Working is over");
}
}
}
最終異步調用的結果如下: