C#中的delegate(委託)和event(事件)

C#中的delegate(委託)和event(事件)

C#中的delegate(委託)和event(事件)


資料來源:

http://www.itivy.com/ivy/archive/2011/8/5/csharp-delegate-and-event.html
https://www.twblogs.net/a/5b7cc3582b71770a43dc786b
https://hackmd.io/@BKLiang/csharp_delegate_event


    孩子,C/C++總學過吧,如果你學得不像我那麼差的話,函數指針總用過吧,就算沒用過總聽說過吧,嗯,大膽的告訴你,你完全可以把delegate理解成C中的函數指針,它允許你傳遞一個類A的方法m給另一個類B的對象,使得類B的對象能夠調用這個方法m,說白了就是可以把方法當作參數傳遞。


    不過delegate和函數指針還是有點區別的,delegate有許多函數指針不具備的優點:


        首先,函數指針只能指向靜態函數,而delegate既可以引用靜態函數,又可以引用非靜態成員函數。


        在引用非靜態成員函數時,delegate不但保存了對此函數入口指針的引用,而且還保存了調用此函數的類實例的引用。


        其次,與函數指針相比,delegate是面向對象、類型安全、可靠的受控(managed)對象。

        也就是說,runtime能夠保證delegate指向一個有效的方法, 你無須擔心delegate會指向無效地址或者越界地址。


有什麼能比舉個例子更能說明問題呢,代碼纔是硬道理,來吧,看幾個例子吧:

範例01.

public class DelegateTest
{
     // 聲明delegate對象 
     public delegate void CompareDelegate(int a,int b);
     // 欲傳遞的方法,它與CompareDelegate具有相同的參數和返回值類型 
     public static void Compare(int a,int b)
     {
         Console.WriteLine((a>b).ToString());
     }
 
     public static void Main()
     {
         // 創建delegate對象 
         CompareDelegate cd = new CompareDelegate(DelegateTest.Compare);
         // 調用delegate 
         cd(1,2);
     }
}


範例02.

public delegate void MyTestDelegate(int i);
public class Program
{
    public static void Main()
    {
        //創建delegate
        ReceiveDelegateArgsFunc(new MyTestDelegate(DelegateFunction));
    }
    //這個方法接收一個delegate類型的參數,也就是接收一個函數作爲參數
    public static void ReceiveDelegateArgsFunc(MyTestDelegate func)
    {
        func(21);
    }
    //欲傳遞的方法
    public static void DelegateFunction(int i)
    {
        System.Console.WriteLine("傳過來的參數爲: {0}.", i);
    }
}


範例03.

//這裏自定義一個EventArgs,因爲我想知道Clicker
public class ButtonClickArgs : EventArgs
{
    public string Clicker;
}
 
public class MyButton
{
    //定義一個delegate委託
    public delegate void ClickHandler(object sender, ButtonClickArgs e);
    //定義事件,類型爲上面定義的ClickHandler委託
    public event ClickHandler OnClick;
 
    public void Click()
    {
        //...觸發之前可能做了n多操作
        //.....
 
        //這時觸發Click事件,並傳入參數Clicker爲本博主ivy
        OnClick(this, new ButtonClickArgs() { Clicker = "ivy" });
    }
}
 
public class Program
{
    public static void Main()
    {
        MyButton btn = new MyButton();
 
        //註冊事件,把btn_OnClick方法壓入事件隊列,
        //可以+=多個,這裏簡單點就壓入一個吧。
        btn.OnClick += new MyButton.ClickHandler(btn_OnClick);
    }
 
    //怎麼看到這個函數很熟悉吧,就是你原來雙擊button自動產生的代碼
    public static void btn_OnClick(object sender, ButtonClickArgs e)
    {
        Console.WriteLine("真賤,我居然被ivy點擊了!");
    }
}


範例04.

/*
* delegate (回傳型別) [委派類別] (參數型別..)
* 宣告一個委派類別 - ArithmeticExpr
* 傳入方法(method)的回傳型別為void,參數型別為兩個int
*/
public delegate void ArithmeticExpr(int a, int b);

// 顯示出該方法的名稱與 a + b 的結果
public static void Add(int a, int b)
{
    Console.WriteLine("Method:[Add] has been called.\nThe answer is: " + (a + b).ToString() + "\n");
}

// 顯示出該方法的名稱與 a - b 的結果
public static void Sub(int a, int b)
{
    Console.WriteLine("Method:[Sub] has been called.\nThe answer is: " + (a - b).ToString() + "\n");
}

// 顯示出該方法的名稱與 a * b 的結果
public static void Multiply(int a, int b)
{
    Console.WriteLine("Method:[Multiply] has been called.\nThe answer is: " + (a * b).ToString() + "\n");
}

static void Main(string[] args)
{
    // 宣告三個委派
    ArithmeticExpr ar1, ar2, ar3;

    /*******************Section 1*******************/
    ar1 = Add;          // 起始將Add指派給ar1
    ar1(10, 7);         // Invoke委派,呼叫Add並顯示出答案: 17
    ar1 = Sub;          // ar1指派為Sub
    ar1(10, 7);         // Invoke委派,呼叫Sub並顯示出答案: 3
    ar1.Invoke(10, 7);  // 或是可以使用Invoke方法
    
    /*******************Section 2*******************/
    ar2 = Add;  // 起始將Add指派給ar2
    ar2(3, 41); // Invoke委派,呼叫Add並顯示出答案: 44
    ar2 += Sub; // 將Sub方法加入ar2的方法清單中
                // 現在方法清單中有Add和Sub (順序為: Add -> Sub)
    ar2(3, 41); // Invoke委派,首先呼叫Add,答案為44;而後呼叫Sub,答案為-38

    /*******************Section 3*******************/
    ar3 = Multiply; // 起始將Multiply指派給ar3
    ar3 += Add;     // 將Add方法加入ar3的方法清單中
                    // 現在方法清單中有Multiply和Add (順序為: Multiply -> Add)
    ar3 += Sub;     // 將Sub方法加入ar3的方法清單中
                    // 現在方法清單中有Multiply、Add和Sub (順序為: Multiply -> Add -> Sub)
    ar3(67, 19);    // Invoke委派,依序呼叫 Multiply、Add和Sub
    ar3 -= ar2;     // 將Add、Sub移出方法清單
    ar3(67, 19);    // 再次Invoke委派,這次只有ar3被呼叫
    ar3 = null;     // 清空整個方法清單
    ar3(18, 0);     // ERROR ==> 方法清單已經被清空了,拋出例外
            
    Console.ReadKey();
}


範例05.

class ObjectA
{
    public string Name { get; set; }
    public int CallCountThreshold { get; set; }

    private int _callCount;

    public delegate void ObjADelegate(ObjectA objectA);
    public event ObjADelegate ObjAnounceEvent;

    public ObjectA(string name, int callCountThreshold)
    {
        Name = name;
        CallCountThreshold = callCountThreshold;
        _callCount = 0;
    }

    public void OnObjAEventBeenCalled()
    {
        _callCount += 1;

        if (_callCount > CallCountThreshold)
        {
            ObjAnounceEvent = null;
            Console.WriteLine("Clear the invocation list.");
            Environment.Exit(0);
        }

        ObjAnounceEvent?.Invoke(this);
    }
}

class ObjectB
{
    public void ObjBGreeting(ObjectA objectA)
    {
        Console.WriteLine("Hello, " + objectA.Name + ". This is Object B.");
    }
}

class ObjectC
{
    public void ObjCGreeting(ObjectA objectA)
    {
        Console.WriteLine("I love you, " + objectA.Name + ". This is Object C.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        ObjectA objectA = new ObjectA("Kawaii", 5);
        ObjectB objectB = new ObjectB();
        ObjectC objectC = new ObjectC();

        objectA.ObjAnounceEvent += objectB.ObjBGreeting;
        objectA.ObjAnounceEvent += objectC.ObjCGreeting;

        while (Console.ReadKey().Key == ConsoleKey.Spacebar)
        {
            objectA.OnObjAEventBeenCalled();
        }
    }
}


範例06.

// 使用內建的Func進行改寫,最後一個泛型類別為回傳值
public System.Action<int, int> ArithmeticExpr;

// 顯示出該方法的名稱與 a + b 的結果
public static void Add(int a, int b)
{
    Console.WriteLine("Method:[Add] has been     called.\nThe answer is: " + (a + b).ToString() + "\n");
}

// 顯示出該方法的名稱與 a - b 的結果
public static void Sub(int a, int b)
{
    Console.WriteLine("Method:[Sub] has been called.\nThe answer is: " + (a - b).ToString() + "\n");
}

// 顯示出該方法的名稱與 a * b 的結果
public static void Multiply(int a, int b)
{
    Console.WriteLine("Method:[Multiply] has been     called.\nThe answer is: " + (a * b).ToString() + "\n");
}

/*
System.EventHandler: 內部定義兩種EventHandler

public delegate void EventHandler(object sender, EventArgs e);

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
*/


範例07.

class ObjectA
{
    public string Name { get; set; }
    public int CallCountThreshold { get; set; }

    public int CallCount;

    public EventHandler<ObjectAEventArgs> ObjAnounceEvent;

    public ObjectA(string name, int callCountThreshold)
    {
        Name = name;
        CallCountThreshold = callCountThreshold;
        CallCount = 0;
    }

    public void OnObjAEventBeenCalled()
    {
        CallCount += 1;

        if (CallCount > CallCountThreshold)
        {
            ObjAnounceEvent = null;
            Console.WriteLine("Clear the invocation list.");
            Environment.Exit(0);
        }

        ObjAnounceEvent?.Invoke(this, new ObjectAEventArgs(Name));
    }
}

class ObjectB
{
    public void ObjBGreeting(object sender, ObjectAEventArgs args)
    {
        Console.WriteLine("Hello, " + args.Name + ". This is Object B.");
    }
}

class ObjectC
{
    public void ObjCGreeting(object sender, ObjectAEventArgs args)
    {
        Console.WriteLine("I love you, " + args.Name + ". This is Object C.");
    }
}

class ObjectAEventArgs : EventArgs
{
    public ObjectAEventArgs(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        ObjectA objectA = new ObjectA("Kawaii", 5);
        ObjectB objectB = new ObjectB();
        ObjectC objectC = new ObjectC();

        objectA.ObjAnounceEvent += objectB.ObjBGreeting;
        objectA.ObjAnounceEvent += objectC.ObjCGreeting;

        while (Console.ReadKey().Key == ConsoleKey.Spacebar)
        {
            objectA.OnObjAEventBeenCalled();
        }
    }
}


One thought on “C#中的delegate(委託)和event(事件)

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *