2022年3月17日 星期四

C# 委派 (Delegate) 函數指標

 Introduction

委派可以看成方法的指標,利用委派可以間接叫用方法。

委派通常主要應用於兩方面:

  1. 事件的驅動
  2. 兩個處理程序間互相呼叫(Call Back)。

 

Example

使用委派的三個步驟:

  1. 宣告委派型別。
  • [ public | protected | private ] delegate 回傳值型態 委派名稱 ( 參數群 )

    2.    實體化委派型別並指向相對應方法。

  • 建立委派物件實體時,必須要傳入符合委派規格的方法參考

    3.    使用 Invoke 方法叫用委派。

    另外  : 如果要將新的方法的位置參考加入到委派物件的執行方法清單的話,必須透過 「+=」 關鍵字。

sample1

委派的基本操作


class Program {

        //step1:宣告委派型別。
        public delegate void MyDelegate(string name);

        static void Main(string[] args) {
            //step2:實體化委派型別並指向相對應方法。
            //      MyDelegate 委派,為沒有傳回值,
            //      並且傳入參數為一個字串型別。
            MyDelegate oMyDel = new MyDelegate(Show);

            //.net 2.0 之後可以簡化。
            MyDelegate oYourDel = Show;

            //將方法加入委派物件的執行方法清單中
            oYourDel += new MyDelegate(Show2);

            //step3 : 使用 Invoke 方法叫用委派。
            oMyDel.Invoke("MyHello!");
            oYourDel.Invoke("YourHello!");

            //也可簡化。
            oMyDel("MyHello!");
            oYourDel("YourHello!");

          
            Console.ReadKey();
        }

        public static void Show(string value) {
            Console.WriteLine("Show : {0}",value);
        }

        public static void Show2(string value) {
            Console.WriteLine("Show2 : {0}",value);
        }
    }

結果

tmp

 

 

sample2

匿名方法 (Anonymous method) 是 .net 2.0 的新功能,當執行委派所指定的方法是一些名稱不太重要的

方法時,可以省略方法名稱。


///不使用匿名方法
public delegate MyShow(string m);

public void show(string value){
   MessageBox.show(value);	
}

private void Form1_Load(object sender,EventArgs e){
   MyShow oMyDel = show;
   oMyDel.Invoke("不使用匿名方法");
}

 


///使用匿名方法
public delegate void MyShow(string m)

private void Form1_Load(object sender,EventArts e){
     MyShow oMyDel = delegate(string m){
          MessageBox.Show(m); 
     };
     oMyDel.Invoke("使用匿名方法");
}

 

 

 

sample3

多重傳送委派  是單一事件引發多個事件,利用 「-」與「+」號完成委派的新增與刪除。

其中,以下兩個方法得到的結果會是一樣的。


//直接加方法
del d = method1;
d = d + method2;
d.Invoke();

//直接加委派
del d1 = method1;
del d2 = method2;
d1 += d2;
d1.Invoke();

 


public delegate void Del();

        private static void Show() {
            Console.WriteLine("第一個呼叫");
        }

        private static void Show2() {
            Console.WriteLine("第二個呼叫");
        }

        private static void Show3() {
            Console.WriteLine("第三個呼叫");
        }

        static void Main(string[] args) {
            Del oMyDel = Show;
            oMyDel = oMyDel + Show2;
            oMyDel += Show3;
            oMyDel.Invoke();

            Console.ReadKey();
        }

結果

tmp

 

sample4

實現 CallBack


//宣告委派型別
    public delegate string MyDel();

    class A {
        private string _name = "我是 A";

        //建立與委派型別對應的方法
        public string showInfo() {
            return this._name;
        }

        public A() {
            //建立委派驅動
            DelDriver dirver = new DelDriver();
            //建立委派實例並且指定方法
            MyDel d1 = showInfo;
            //呼叫驅動方法並且傳入委派物件
            dirver.delDirver(d1);
        }
    }

    class B {
        private string _name = "我是 B";

        //建立與委派型別對應的方法
        public string showInfo() {
            return this._name;
        }

        public B() {
            DelDriver dirver = new DelDriver();
            MyDel d1 = showInfo;
            dirver.delDirver(d1);
        }
    }

    class DelDriver {
        public void delDirver(MyDel del) {
            Console.WriteLine(del.Invoke());
        }
    }

    class Program {
        static void Main(string[] args) {
            A a = new A();
            B b = new B();

            Console.ReadKey();
        }
    }

結果

tmp

 

sample5

利用 Lambda 運算式 完成委派,這個方法僅適用 .net 3.0 之後的版本。

Lambda 運算式」(Lambda Expression) 是一種匿名函式,它可以包含運算式和陳述式 (Statement),而且可以用來建立委派 (Delegate) 或運算式樹狀架構型別。

所有的 Lambda 運算式都會使用 Lambda 運算子 =>,意思為「移至」。Lambda 運算子的左邊會指定輸入參數 (如果存在),右邊則包含運算式或陳述式區塊。


 //宣告委派型別
        public delegate void Del(string m);

        static void Main(string[] args) {            
            //建立委派物件
            //大括號裡面為匿名方法,左方 s 變數對應匿名方法,傳入。
            Del oMyDel = (string s) => { Console.WriteLine(s); };
            oMyDel("Hello!");
            Console.ReadKey();
        }

結果

三小俠

 

 

Link

委派 (C# 程式設計手冊)

HOW TO:宣告、產生和使用委派 (C# 程式設計手冊)

使用具名和匿名方法委派的比較 (C# 程式設計手冊)

使用委派取代介面的時機 (C# 程式設計手冊)

委派中的 Covariance 和 Contravariance (C# 程式設計手冊)

HOW TO:組合委派 (多點傳送委派) (C# 程式設計手冊)

Lambda 運算式 (C# 程式設計手冊)

匿名方法 (C# 程式設計手冊)

 

<<後記>>

1. 使用委派建立執行緒參數

    class Program {
        static void Main(string[] args) {

            StartThread();
            StartThread2();
            Console.ReadKey();
        }

        static void StartThread() {
            //使用匿名方法,建立委派不具參數的委派
            System.Threading.Thread t1 = new System.Threading.Thread
             (delegate() {                
                System.Console.WriteLine("World!");
            });
            t1.Start();
            
        }

        static void StartThread2() {
            //使用匿名方法,建立帶有參數的委派
            System.Threading.Thread t1 = new System.Threading.Thread
              (delegate(object value) {
                System.Console.Write(value);
                    });
            t1.Start("Hello World!");
        }

    }

 

<<後記2>>

在網路上不小心發現 蔡學鏞 大師有介紹 C# 的函數指標,大家可以參考一下

函數指標的進化論 (上)
函數指標的進化論 (下)

資料來源:https://dotblogs.com.tw/atowngit/2009/12/07/12311

沒有留言: