C# 事件和委托的用途及區別


 

▲簡單委托的構成:

可以選擇將委托類型看做只定義了一個方法的接口,將委托的實例看做實現了那個接口的一個對象。

 

 

1. 聲明委托類型——定義委托

混亂的根源:容易產生歧義的“委托”
委托經常被人誤解,這是由於大家喜歡用委托這個詞來描述委托類型和委托實例。
Console.WriteLine("StringProcessor父類:" + typeof(StringProcessor).BaseType);//StringProcessor父類:System.MulticastDelegate

 

委托的基類:System.MulticastDelegate——的基類:System.Delegate

2. 為委托實例的操作找到一個恰當的方法

3、創建委托實例

需要一個方法以及(對於實例方法來說)調用方法的目標;

單純創建一個委托實例卻不在某一時刻調用它是沒有什么意義的。看看最后一步——調用。

4. 調用委托實例

“委托實例被調用”中的“調用”對應的是invoke。
invoke理解為“喚出”某個東西來幫你調用一個信息不明的方法時,用invoke比call恰當。理解為喚出和調用區別不明顯。
調用委托實例的兩種方式——顯式調用Invoke和使用C#的簡化形式。一般情況下只需使用簡化形式。

  1. 聲明的委托實例.invoke(參數)
  2. 聲明的委托實例(參數)

委托的實質是間接完成某種操作,這增大了復雜性(看看為了輸出這點兒內容,用了多少行代碼),但同時也增加了靈活性。

5.合並和刪除委托

System.Delegate類型的靜態方法Combine和Remove負責創建新的委托實例。
其中, Combine等價於+=,而Remove等價於-=

 

 

 

委托是不易變的:

委托實例類似於string,創建了委托實例后,有關他的一切就不能改變。

 

 

 

事件和委托的用途

委托delegate:

用途最廣的是,使用委托為形參,傳遞實參(回調函數)時,可以使用匿名函數。對比Java:java使用匿名函數是形參是接口,創建一個接口實現類匿名類,匿名類實現接口方法。

例如:

var t4 = Task.Run(() => TaskMethod.DoTask("using Run method"));
//系統方法Run 就使用了委托作為參數
public static Task<TResult> Run<[NullableAttribute(2)] TResult>(Func<TResult> function);

事件event:

開發者經常將事件和委托實例,或者將事件和委托類型的字段混為一談。之所以產生混淆,原因和以前相同,因為C#提供了一種簡寫方式,允許使用字段風格的事件field-like event

字段風格的事件使所有這些的實現變得更易閱讀,只需一個聲明就可以了。 編譯器會將聲明轉換成一個具有默認add/remove實現的事件和一個私有委托類型的字段

事件不是委托實例——只是成對的add/remove方法(類似於屬性的取值方法/賦值方法)事件包含一個私有委托類型字段。

 

void OnEventRaised(object sender, EventArgs args);

 

 

 

 

發布器類:

  • 聲明委托,事件委托一般命名為:NameEventHandler
  • 聲明事件

 

//event關鍵字代表事件,返回類型為委托;
public event BoilerLogHandler BoilerEventLog;//基於委托定義事件,委托的函數指針。
  • 創建引發事件的方法,一般命名為:OnEventName。

訂閱器類

  • 實例化發布器類
  • 綁定事件:+= 委托(或函數都可以)

發布器類一個方法OnEventName() 等價於,訂閱事件中的所有綁定方法一起執行。(廣播)

public class Host
{
//定義委托原型
    public delegate void OpenDoorEventHandler();
//定義委托類型的事件
    public event GoHomeEventHandler OpenDoor;
//定義內部一個方法,在這個方法內判斷,OpenDoor事件是否被其他對象注冊,一旦注冊了,則調用執行事件。
    protected void OnOpenDoor()
    {
        if(OpenDoor!=null)
        {
            OpenDoor();
        }
    }
    public void GoHome()
    {
        OnOpenDoor();
    }
}
public class Cat
{
    public void Run()
    {
        //貓跑了
    }
}
public class Dog
{
    public void Bark()
    {
        //狗叫了
    }
}
如何使用如下:
Host 主人 =new Host();
Cat 貓 = new Cat();
Dog 狗 = new Dog();

主人.OpenDoor += new 主人.OpenDoorEventHandler(貓.Run);
主人.OpenDoor += new 主人.OpenDoorEventHandler(狗.Back);

主人.GoHome【發布器類】就等價於
貓.Run();//【訂閱器類】
狗.Back();//【訂閱器類】
被觀察者【發布器類】
,做出某一特定“動作”(被觀察者【發布器類】的特定“動作”【發布器類中引發事件的方法,一般命名為:OnEventName】,注冊了N個不同對象的不同反映【訂閱器類】),觀察者【訂閱器類】對這個特定“動作”做出不懂的反映。
 

2.2 事件用法三步曲

事件機制的使用方法可以歸納為3個步驟:

(1)事件發布者定義event以及事件相關信息  (2)事件偵聽者訂閱event   (3)事件發布者觸發event,自動調用訂閱者的事件處理方法。

到這里可能有的小伙伴覺得 和 多播委托 一樣,添加也是+=,取消也是-=

其實事件就是多播委托的一種封裝,如果不使用事件,直接使用委托,能不能實現事件機制呢?答案是完全可以。

那么,為什么還要用事件呢?事件是怎么封裝了委托呢?

實際上,在以上案例中,使用 event 關鍵字在一行上定義事件是C# 提供的事件的簡化記法。在我們定義了上述事件后,編譯器會自動生成如下代碼:

private EventHandler<CarInfoEventArgs> newCarEvent;
public event EventHandler<CarInfoEventArgs> NewCarEvent
{
    add
    {
        newCarEvent += value;
    }
    remove
    {
        newCarEvent -= value;
    }
}

 


這非常像字段及其屬性的關系。注意到,委托字段 newCarEvent 是私有的,因此在外部不能直接為事件賦值,但可以通過公開的 += 和 -= 運算符為事件添加實例方法。 另外,事件 event 是一種數據類型,是一個已經聲明的委托類,只能在某個類的內部聲明,並且只能被該類調用,不能在命名空間中聲明和使用。這一點與委托 delegate 的聲明不同。

總結:事件與委托的區別在於兩點:

(1)委托是一個類,可以在命名空間中聲明;而事件只能在事件發布者內部定義,且只能被該類調用。

(2)可以直接使用一個方法為委托賦值,而事件只開放了 += 和 -= 運算符為其添加或刪除方法。
————————————————
版權聲明:本文為CSDN博主「wnvalentin」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/wnvalentin/article/details/82254656

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM