溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#開發(fā)中事件與委托機(jī)制的示例分析

發(fā)布時間:2021-12-03 10:20:50 來源:億速云 閱讀:109 作者:小新 欄目:編程語言

這篇文章給大家分享的是有關(guān)C#開發(fā)中事件與委托機(jī)制的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

概述

C#中的委托類似于C或C++中的函數(shù)指針。使用委托使程序員可以將方法引用封裝在委托對象內(nèi)。然后可以將該委托對象傳遞給可調(diào)用所引用方法的代碼,而不必在編譯時知道將調(diào)用哪個方法。與C或C++中的函數(shù)指針不同,委托是面向?qū)ο?,而且是類型安全的?/p>

C#中的“事件”是當(dāng)對象發(fā)生某些事情時,類向該類的客戶提供通知的一種方法。事件最常見的用途是用于圖形用戶界面;通常,表示界面中的控件的類具有一些事件,當(dāng)用戶對控件進(jìn)行某些操作(如單擊某個按鈕)時,將通知這些事件。

使用委托來聲明事件。委托對象封裝一個方法,以便可以匿名調(diào)用該方法。事件是類允許客戶為其提供方法(事件發(fā)生時應(yīng)調(diào)用這些方法)的委托的一種方法。事件發(fā)生時,將調(diào)用其客戶提供給它的委托。

注明:委托是對方法的包裝 在不確定要調(diào)用什么方法時候而又不能用抽象或者多態(tài)實(shí)現(xiàn)的時候用委托。

public interface PilotLamp     {    /// <summary>    /// green light    /// </summary>    void TurnOn();     /// <summary>     /// notice     /// </summary>     string Notice     {     get;    set;     }     }

先創(chuàng)建PilotLamp.cs

再創(chuàng)建DelegateEvent.cs:

public delegate void EventHandler();

再創(chuàng)建TrafficLight.cs:

public class TrafficLight : PilotLamp     {     public event EventHandler Notices;    private string notice;    #region GreenLight 成員     public void TurnOn()     {     if (Notices != null)     Notices();     }     public string Notice     {    get    {     return notice;     }    set    {     notice = value;     }     }    #endregion     }

再創(chuàng)建Driver.cs

public class Driver        private string Name;    private PilotLamp greenLight;    public Driver(string name, PilotLamp greenLight)    {    this.Name = name;    this.greenLight = greenLight;    }    public void GoLeft()    {    Console.WriteLine(string.Format("{1}司機(jī),{0},請向左開車.", greenLight.Notice, Name));    }    }

再創(chuàng)建Pedestrian.cs:

 public class Pedestrian     {     private string Name;    private PilotLamp greenLight;    public Pedestrian(string name, PilotLamp greenLight)    {     this.Name = name;     this.greenLight = greenLight;     }    public void GoThrough()     {     Console.WriteLine( string.Format("{0}同志,{1},請向前走.", Name, greenLight.Notice));     }     }

***再調(diào)用:

 public partial class Run : Form    {    public Run()     {     InitializeComponent();    }    private void btnRun_Click(object sender, EventArgs e)     {    //-------------------------------------    TrafficLight trafficLight = new TrafficLight();     Driver driverOne = new Driver("張三", trafficLight);    Driver driverTwo = new Driver("李四", trafficLight);    Pedestrian pedestrianOne = new Pedestrian("王五", trafficLight);     Pedestrian pedestrianTwo = new Pedestrian("麻六", trafficLight);     trafficLight.Notices += new Observer.EventHandler(driverOne.GoLeft);     trafficLight.Notices += new Observer.EventHandler(driverTwo.GoLeft);     trafficLight.Notices += new Observer.EventHandler(pedestrianOne.GoThrough);    trafficLight.Notices += new Observer.EventHandler(pedestrianTwo.GoThrough);     trafficLight.Notice = "綠燈亮了.";     trafficLight.TurnOn();    //-------------------------------------     }     }

輸出時選控制臺應(yīng)用程序如圖:

C#開發(fā)中事件與委托機(jī)制的示例分析

結(jié)果如下圖:

C#開發(fā)中事件與委托機(jī)制的示例分析

事件的使用示例:

 namespace DelegateAndEvent     {     class Program     {     static void Main(string[] args)    {    Publishser pub = new Publishser();    OneScriber oneSub = new OneScriber();     TwoScriber twoSub = new TwoScriber();    ThreeScriber threeSub = new ThreeScriber ();     pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged);     pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged);     pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged);    pub.DoSomething();    }     }     public delegate string GeneralEventHandler();     public class Publishser     {     public event GeneralEventHandler NumberChanged;    public void DoSomething()    {     if (NumberChanged != null)     {     Delegate[] generalEventHandlers = NumberChanged.GetInvocationList();    foreach (Delegate generalEventHandler in generalEventHandlers)     {    GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler;     string rtn = mothed();     Console.WriteLine(rtn);     System.Threading.Thread.Sleep(2000);     }     }     }     }     public class OneScriber     {     public string OnNumberChanged()     {     return "One Subscriber";     }     }     public class TwoScriber     {     public string OnNumberChanged()     {     return "Two Subscriber";    }     }     public class ThreeScriber     {     public string OnNumberChanged()     {     return "Three Subscriber";     }     }     }

運(yùn)行結(jié)果:

C#開發(fā)中事件與委托機(jī)制的示例分析

注意到Delegate是GeneralEventHandler 的基類,所以為了觸發(fā)事件,先要進(jìn)行一個向下的強(qiáng)制轉(zhuǎn)換,之后才能在其上觸發(fā)事件,調(diào)用所有注冊對象的方法。除了使用這種方式以外,還有一種更靈活方式可以調(diào)用方法,它是定義在Delegate基類中的DynamicInvoke()方法:

public object DynamicInvoke(params object[] args);

這可能是調(diào)用委托最通用的方法了,適用于所有類型的委托。它接受的參數(shù)為object[],也就是說它可以將任意數(shù)量的任意類型作為參數(shù),并返回單個object對象。上面的DoSomething()方法也可以改寫成下面這種通用形式:

代碼作如下改動:

namespace DelegateAndEvent    {     class Program     {     static void Main(string[] args)    {     Publishser pub = new Publishser();    OneScriber oneSub = new OneScriber();     TwoScriber twoSub = new TwoScriber();     ThreeScriber threeSub = new ThreeScriber();     pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged);     pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged);    pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged);     List<string> strlist = pub.DoSomething();    foreach (string result in strlist)     Console.WriteLine(result);     System.Threading.Thread.Sleep(5000);     }     }    public delegate string GeneralEventHandler();     public class Publishser    {    public event GeneralEventHandler NumberChanged;     public List<string> DoSomething()     {     List<string> strList = new List<string>();    if (NumberChanged == null) return strList;     Delegate[] generalEventHandlers = NumberChanged.GetInvocationList();     foreach (Delegate generalEventHandler in generalEventHandlers)    {     // GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler;     string rtn = generalEventHandler.DynamicInvoke(null).ToString();     strList.Add(rtn);    }    return strList;    }     }     public class OneScriber    {     public string OnNumberChanged()    {     return "One Subscriber";     }     }     public class TwoScriber     {     public string OnNumberChanged()     {     return "Two Subscriber";     }     }     public class ThreeScriber     {    public string OnNumberChanged()     {    return "Three Subscriber";     }     }     }

結(jié)果如下:

C#開發(fā)中事件與委托機(jī)制的示例分析

還是一樣的結(jié)果.

委托的定義會生成繼承自MulticastDelegate的完整的類,其中包含Invoke()、BeginInvoke()和EndInvoke()方法。當(dāng)我們直接調(diào)用委托時,實(shí)際上是調(diào)用了Invoke()方法,它會中斷調(diào)用它的客戶端,然后在客戶端線程上執(zhí)行所有訂閱者的方法(客戶端無法繼續(xù)執(zhí)行后面代碼),***將控制權(quán)返回客戶端。注意到BeginInvoke()、EndInvoke()方法,在.Net中,異步執(zhí)行的方法通常都會配對出現(xiàn),并且以Begin和End作為方法的開頭(最常見的可能就是Stream類的BeginRead()和EndRead()方法了)。它們用于方法的異步執(zhí)行,即是在調(diào)用BeginInvoke()之后,客戶端從線程池中抓取一個閑置線程,然后交由這個線程去執(zhí)行訂閱者的方法,而客戶端線程則可以繼續(xù)執(zhí)行下面的代碼。

BeginInvoke()接受“動態(tài)”的參數(shù)個數(shù)和類型,為什么說“動態(tài)”的呢?因?yàn)樗膮?shù)是在編譯時根據(jù)委托的定義動態(tài)生成的,其中前面參數(shù)的個數(shù)和類型與委托定義中接受的參數(shù)個數(shù)和類型相同,***兩個參數(shù)分別是AsyncCallback和Object類型,對于它們更具體的內(nèi)容,可以參見下一節(jié)委托和方法的異步調(diào)用部分?,F(xiàn)在,我們僅需要對這兩個參數(shù)傳入null就可以了。另外還需要注意幾點(diǎn):

在委托類型上調(diào)用BeginInvoke()時,此委托對象只能包含一個目標(biāo)方法,所以對于多個訂閱者注冊的情況,必須使用GetInvocationList()獲得所有委托對象,然后遍歷它們,分別在其上調(diào)用BeginInvoke()方法。如果直接在委托上調(diào)用BeginInvoke(),會拋出異常,提示“委托只能包含一個目標(biāo)方法”。

如果訂閱者的方法拋出異常,.NET會捕捉到它,但是只有在調(diào)用EndInvoke()的時候,才會將異常重新拋出。而在本例中,我們不使用EndInvoke()(因?yàn)槲覀儾魂P(guān)心訂閱者的執(zhí)行情況),所以我們無需處理異常,因?yàn)榧词箳伋霎惓#彩窃诹硪粋€線程上,不會影響到客戶端線程(客戶端甚至不知道訂閱者發(fā)生了異常,這有時是好事有時是壞事)

BeginInvoke()方法屬于委托定義所生成的類,它既不屬于MulticastDelegate也不屬于Delegate基類, 我們需要進(jìn)行一個向下轉(zhuǎn)換,來獲取到實(shí)際的委托類型。

示例:

namespace DelegateAndEvent     {     class Program     {     static void Main(string[] args)     {     Publishser pub = new Publishser();     OneScriber oneSub = new OneScriber();    TwoScriber twoSub = new TwoScriber();     ThreeScriber threeSub = new ThreeScriber();     pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged);     pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged);     pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged);    List<string> strlist = pub.DoSomething();     foreach (string result in strlist)    Console.WriteLine(result);     System.Threading.Thread.Sleep(5000);     }    }     public delegate string GeneralEventHandler(object sender,EventArgs e);     public class Publishser     {    public event GeneralEventHandler NumberChanged;    public List<string> DoSomething()     {    List<string> strList = new List<string>();    if (NumberChanged == null) return strList;     Delegate[] generalEventHandlers = NumberChanged.GetInvocationList();     foreach (Delegate generalEventHandler in generalEventHandlers)     {     GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler;     IAsyncResult result = mothed.BeginInvoke(this, EventArgs.Empty, null, null);    string str = mothed.EndInvoke(result);     strList.Add(str);    }     return strList;    }     }     public class OneScriber     {    public string OnNumberChanged(object sender,EventArgs e)    {     return "One Subscriber";     }    }     public class TwoScriber     {    public string OnNumberChanged(object sender, EventArgs e)     {    return "Two Subscriber";     }     }     public class ThreeScriber     {    public string OnNumberChanged(object sender, EventArgs e)    {     return "Three Subscriber";    }     }     }

結(jié)果:

C#開發(fā)中事件與委托機(jī)制的示例分析

BeginInvoke的另外兩個參數(shù)分別是AsyncCallback和Object類型,其中AsyncCallback是一個委托類型,它用于方法的回調(diào),即是說當(dāng)異步方法執(zhí)行完畢時自動進(jìn)行調(diào)用的方法。它的定義為:

public delegate void AsyncCallback(IAsyncResult ar);

Object類型用于傳遞任何你想要的數(shù)值,它可以通過IAsyncResult的AsyncState屬性獲得。

感謝各位的閱讀!關(guān)于“C#開發(fā)中事件與委托機(jī)制的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI