日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C# 深入浅出 委托与事件

發布時間:2025/3/14 C# 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C# 深入浅出 委托与事件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C#中的委托和事件的概念接觸很久了,但是一直以來總沒有特別透徹的感覺,現在我在這里總結一下:

首先我們要知道委托的由來,為什么要使用委托了?

我們先看一個例子:

假設我們有這樣一個需求,需要計算在不同方式下的總價,如下面代碼所示,這里假設只有兩種方式,一種是正常價格,一種是折扣價格:

1 public enum CalcMethod 2 { 3 Normal, 4 Debate 5 } 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 CalcPrice(10, 100,CalcMethod.Normal); 11 CalcPrice(10, 100, CalcMethod.Debate); 12 Console.ReadLine(); 13 } 14 15 /// <summary> 16 /// 計算總價 17 /// </summary> 18 /// <param name="count">數量</param> 19 /// <param name="price">單價</param> 20 public static double CalcPrice(int count,int price,CalcMethod method) 21 { 22 switch (method) 23 { 24 case CalcMethod.Normal: 25 return NormalPrice(count, price); 26 case CalcMethod.Debate: 27 return DebatePrice(count, price); 28 default: 29 return 0; 30 } 31 } 32 33 public static double NormalPrice(int count,int price) 34 { 35 Console.WriteLine("正常的價格是:{0}", count * price); 36 return count * price; 37 } 38 public static double DebatePrice(int count, int price) 39 { 40 Console.WriteLine("折扣的價格是:{0}", count * price*0.7); 41 return count * price*0.7; 42 } 43 } View Code

但是我們想一想,如果還要增加總價計算方式,那么我們是不是要不斷的修改CalcPrice方法,CalcMethod枚舉呢?
那么是不是有更好的方式呢?

我們可以把真正的計算價格的方式委托給一個函數來計算,這樣委托就誕生了。

首先我們定義一個跟方法參數和返回值類型一樣的委托類型:

?public delegate double CalcPriceDelegate(int count, int price);

然后修改計算方法:

??????? /// <summary>
??????? /// 計算總價
??????? /// </summary>
??????? /// <param name="count">數量</param>
??????? /// <param name="price">單價</param>
??????? public static double CalcPrice(int count, int price, CalcPriceDelegate calcDelegate)
??????? {
??????????? return calcDelegate(count, price);
??????? }

然后調用的時候直接用簽名相同的方法傳遞就可以了:

???????? ? CalcPrice(10, 100, NormalPrice);
??????????? CalcPrice(10, 100, DebatePrice);

?????到這里我們大體明白了委托可以使用方法作為參數,這樣就避免了程序中出現大量的條件分支語句,程序的擴展性好。

???接下來我要對委托做一個深入的探討:

  委托首先其實也是一個類,

????????? public delegate double CalcPriceDelegate(int count, int price);

  上面這句話其實就是申明一種委托類型,這個類型在編譯的時候會生成以下成員:

    1)public?extern CalcPriceDelegate(object @object, IntPtr method);

      第一個參數是記錄委托對象包裝的實例方法所在的對象(this,如果包裝的是靜態方法,就為NULL),第二個參數就是表示要回調的方法。

    2) public virtual extern double Invoke(int count, int price);//同步調用委托方法

    3)public virtual extern IAsynResult BeginInvoke(int count, int price, AsyncCallback callback, object @object);??這個是異步執行委托方法,前面兩個參數是委托的方法的輸入參數,callback是回調方法,也就是說方法執行完成后的回調方法,AsyncCallback本身也是一個委托類型,其原型是:

     public delegate void AsyncCallback(IAsyncResult ar);

    ??最后一個參數是回調所需要的輸入參數,這個參數會隱含在IAsyncResult的AsyncState中。

    另外返回值也是一個IAsyncResult結果。

????????????與之相對應的,public virtual extern double?EndInvoke(IAsyncResult result)

    結束異步回調。

    以前對IAsyncResult,還有AsyncCallback都有點陌生,其實我們可以這樣來理解,我想要一個方法異步來執行,那么肯定就需要調用BeginInvoke,那么我如何又能知道什么時候這個異步的調用結束呢?這就需要用到AsyncCallback這個異步回調,如果這個回調需要參數,就賦值給object,如果不確定是否異步執行完,就要用EndInvoke來確保結束,輸入參數就是BeginInvoke的返回值IAsynResult,就相當于BeginInvoke的時候開出了一個收據,EndInvoke又把這個收據還了。

??????????這個話題要想深入下去就太多了,我們還是回到委托上來,委托時一個類,其編譯后就是這么些個成員。

   我平時調用的時候經常會被各種各樣的調用方法給搞糊涂了,這里總結下各種調用方式:

    ?假設委托類型為:public delegate double CalcPriceDelegate(int count, int price);?這就相當于定義了一個類,

    接下來就是賦值了(相當于申明對象):

    1)CalcPriceDelegate calcDelegate = new CalcPriceDelegate(NormalPrice).?這是最完整的賦值方式。

    2)?CalcPriceDelegate calcDelegate = NormalPrice;?直接賦值方法,編譯器會自動幫我們構造成第一種賦值方式。

    賦值完成后接下來就是如何調用了:

    1)calcDelegate(10,100);

    2)calcDelegate.Invoke(10,100)??與1)方法是一樣的。

    3)calcDelegate.BeginInvoke(10,100,null,null)?異步執行

    委托還可以通過+=來添加方法,委托給多個方法,但是第一個必須是=,否則沒有初始化,相對的,可以使用-=來移除方法。

    至于匿名委托,lambda表達式是一樣的,把握本質就可以了,還需要了解MS定義的委托類型,這里暫時不講了。

?

    接下來講事件:

    第一步,我們還是引用上面的例子,只是把相關的代碼放到一個類里面:

    

1 public delegate double CalcPriceDelegate(int count, int price); 2 public class CalcPriceClass 3 { 4 public double Calc(int count, int price, CalcPriceDelegate calcDelegate) 5 { 6 return calcDelegate(count, price); 7 } 8 } View Code

????????其中主函數里面的調用如下:

1 Console.WriteLine("演示引入事件的第一步:"); 2 CalcPriceClass cp = new CalcPriceClass(); 3 cp.Calc(10, 100, NormalPrice); 4 cp.Calc(10, 100, DebatePrice); 5 Console.ReadLine(); View Code

??????????這種方法,我們破壞了對象的封裝性,我們可以把委托類型的變量放到CalcPriceClass類里面。
于是我們就有了第二步:

???

1 public class CalcPriceClass2 2 { 3 public CalcPriceDelegate m_delegate; 4 public double Calc(int count, int price, CalcPriceDelegate calcDelegate) 5 { 6 return calcDelegate(count, price); 7 } 8 } View Code

?其中主函數的調用如下:

1 Console.WriteLine("演示引入事件的第二步:"); 2 CalcPriceClass2 cp2 = new CalcPriceClass2(); 3 cp2.m_delegate = NormalPrice; 4 cp2.m_delegate += DebatePrice; 5 cp2.Calc(10, 100, cp2.m_delegate); 6 Console.ReadLine(); View Code

我們發現其調用有點怪怪的,? cp2.Calc(10, 100, cp2.m_delegate);既然我為cp2的委托對象賦值了,這個時候其實沒有必要再去傳遞這個委托對象了,于是就有了第三步:

1 public class CalcPriceClass3 2 { 3 public CalcPriceDelegate m_delegate; 4 public double Calc(int count, int price) 5 { 6 if (m_delegate != null) 7 return m_delegate(count, price); 8 else return 0; 9 } 10 } View Code

其中主函數的調用如下:

1 Console.WriteLine("演示引入事件的第三步:"); 2 CalcPriceClass3 cp3 = new CalcPriceClass3(); 3 cp3.m_delegate = NormalPrice; 4 cp3.m_delegate += DebatePrice; 5 cp3.Calc(10, 100); 6 Console.ReadLine(); View Code

在這步完成后,貌似達到了我們想要的結果,但是還是有些隱患的,因為我們可以隨意的給委托變量賦值,所以就有了第四步,加上了事件:

1 public class CalcPriceClass4 2 { 3 public event CalcPriceDelegate m_delegate; 4 public double Calc(int count, int price) 5 { 6 if (m_delegate != null) 7 return m_delegate(count, price); 8 else return 0; 9 } 10 } View Code

其中主函數的調用如下:

1 Console.WriteLine("演示引入事件的第四步:"); 2 CalcPriceClass4 cp4 = new CalcPriceClass4(); 3 cp4.m_delegate += NormalPrice; 4 cp4.m_delegate += DebatePrice; 5 cp4.Calc(10, 100); 6 Console.ReadLine(); View Code

我們給委托變量加上event后有什么不一樣呢?
這個時候我們不能直接給這個事件對象進行賦值,因為其內部是一個私有變量了,另外編譯器會增加兩個公共函數,

一個是add_m_delegate(對應+=),?

public void add_m_delegate(CalcPriceDelegate value)

{

  this.m_delegate = (CalcPriceDelegate)Delegate.Combine(this.m_delegate, value);

}

一個是remove_m_delegate(對應-=),

public void remove_m_delegate(CalcPriceDelegate value)

{

  this.m_delegate = (CalcPriceDelegate)Delegate.Remove(this.m_delegate, value);

}

另外內部的私有字段是這樣子的:private CalcPriceDelegate m_delegate;

通過這四步,我們可以知道了從委托到事件的一個過程,其實事件也是委托,只是編譯器會幫我們做一些事情而已。

?

事件?與 Observer設計模式

?

假設一個熱水器,在溫度達到95度以上的時候,警報器報警,并且顯示器顯示溫度。

代碼如下:

1 public class Heater 2 { 3 private int temperature; 4 public void BoilWater() 5 { 6 for (int i = 0; i < 100; i++) 7 { 8 temperature = i; 9 if (temperature > 95) 10 { 11 MakeAlert(temperature); 12 ShowMsg(temperature); 13 } 14 } 15 } 16 private void MakeAlert(int param) 17 { 18 Console.WriteLine("Alarm:滴滴滴,水已經{0}度了", param); 19 20 } 21 private void ShowMsg(int param) 22 { 23 Console.WriteLine("Display:水快開了,當前溫度:{0}度。", param); 24 } 25 } View Code

假設熱水器由三部分組成:熱水器、警報器、顯示器,它們來自于不同廠商并進行了組裝。那么,應該是熱水器僅僅負責燒水,它不能發出警報也不能顯示水溫;在水燒開時由警報器發出警報、顯示器顯示提示和水溫。如果是上面的代碼可能就不合適了,就要使用下面的代碼:

1 public class Heater2 2 { 3 private int temperature; 4 public delegate void BoilHanlder(int param); 5 public event BoilHanlder BoilEvent; 6 public void BoilWater() 7 { 8 for (int i = 0; i < 100; i++) 9 { 10 temperature = i; 11 if (temperature > 95) 12 { 13 if (BoilEvent != null) 14 BoilEvent(temperature); 15 } 16 } 17 } 18 } 19 public class Alarm 20 { 21 public static void MakeAlert(int param) 22 { 23 Console.WriteLine("Alarm:滴滴滴,水已經{0}度了", param); 24 25 } 26 } 27 public class Display 28 { 29 public static void ShowMsg(int param) 30 { 31 Console.WriteLine("Display:水快開了,當前溫度:{0}度。", param); 32 } 33 } View Code

調用的代碼:

1 Console.WriteLine("演示熱水器熱水機報警及顯示水溫 觀察者模式"); 2 Heater2 ht2 = new Heater2(); 3 ht2.BoilEvent += new Heater2.BoilHanlder(Alarm.MakeAlert); 4 ht2.BoilEvent += Display.ShowMsg; 5 ht2.BoilWater(); 6 Console.ReadLine(); View Code

比較以上兩種方式的不同,后面的這種更加符合面向對象的思想,因為作為熱水器而言,主要的工作是熱水,至于報警,顯示溫度是由其他器件來顯示,是需要顯示器,報警器這些觀察者來觀察熱水器這個觀察對象主體的溫度。
到這里為止,我們知道了如何聲明委托類型,申明委托對象,調用委托方法,委托類的實際成員,以及從委托到事件的演變,事件的表象與在編譯后的實際成員,以及作為觀察者模式使用事件的過程。

不過微軟的規范寫法卻不是這樣,下面改用微軟的規范寫法:

1 public class Heater3 2 { 3 public string type = "RealFire 001"; 4 public string area = "China Xian"; 5 private int temperature; 6 public delegate void BoiledEventHandler(object sender,BoiledEventArgs e); 7 public event BoiledEventHandler Boiled; 8 protected virtual void OnBoiled(BoiledEventArgs e) 9 { 10 if (Boiled != null) 11 { 12 Boiled(this, e); 13 } 14 } 15 public void BoilWater() 16 { 17 for (int i = 0; i < 100; i++) 18 { 19 temperature = i; 20 if (temperature > 95) 21 { 22 BoiledEventArgs e = new BoiledEventArgs(temperature); 23 OnBoiled(e); 24 } 25 } 26 } 27 } 28 public class BoiledEventArgs : EventArgs 29 { 30 public readonly int temperature; 31 public BoiledEventArgs(int temp) 32 { 33 temperature = temp; 34 } 35 } 36 public class Alarm3 37 { 38 public void MakeAlert(object sender, BoiledEventArgs e) 39 { 40 Heater3 ht = (Heater3)sender; 41 Console.WriteLine("Alarm:{0}-{1}", ht.area, ht.type); 42 Console.WriteLine("Alarm:滴滴滴,水已經{0}度了:", e.temperature); 43 Console.WriteLine(); 44 } 45 } 46 public class Display3 47 { 48 public static void ShowMsg(object sender, BoiledEventArgs e) 49 { 50 Heater3 ht = (Heater3)sender; 51 Console.WriteLine("Display:{0}-{1}",ht.area,ht.type); 52 Console.WriteLine("Display:水快燒開了,當前溫度:{0}度。", e.temperature); 53 Console.WriteLine(); 54 } 55 56 } View Code

調用代碼:

1 Console.WriteLine("演示熱水器熱水機報警及顯示水溫 符合微軟模式"); 2 Heater3 ht3 = new Heater3(); 3 Alarm3 al3 = new Alarm3(); 4 ht3.Boiled += al3.MakeAlert; 5 ht3.Boiled += Display3.ShowMsg; 6 ht3.BoilWater(); 7 Console.ReadLine(); View Code

微軟的規范寫法,如果要傳遞參數一般使用EventArgs或者其繼承類,且繼承類的命名以EventArgs結尾。
委托類型的名稱以EventHandler結束。

委托的原型定義:有一個void返回值,并接受兩個輸入參數:一個Object 類型,一個 EventArgs類型(或繼承自EventArgs)。

一般這個Object類型指的是觀察的對象,我們可以這樣來記憶,因為委托就相當于方法,其實執行的就是觀察者,那么觀察者總要知道觀察誰,以及觀察所需要的參數。

其實事件還有一些概念:事件訂閱者,事件接收者,事件發送者,這些以后再補充

?

這里引用了這位仁兄的博客:

http://www.tracefact.net/csharp-programming/delegates-and-events-in-csharp.aspx

?代碼:

?http://files.cnblogs.com/files/monkeyZhong/CSharpDelegateAndEvent.zip

?

?

?

?

    

轉載于:https://www.cnblogs.com/monkeyZhong/p/4595444.html

總結

以上是生活随笔為你收集整理的C# 深入浅出 委托与事件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。