1. 程式人生 > >【C#學習】delegate(委託) 和 event(事件)

【C#學習】delegate(委託) 和 event(事件)

C# 中的委託(Delegate)類似於 C 或 C++ 中函式的指標。委託(Delegate) 是存有對某個方法的引用的一種引用型別變數。引用可在執行時被改變。在C#中方法不能作為引數直接傳遞,必須使用委託(用來委託方法)。delegate(委託)是一種特殊的引用型別,它將方法也作為特殊的物件封裝起來,從而將方法作為變數、引數或者返回值傳遞。委託(Delegate)特別用於實現事件和回撥方法。所有的委託(Delegate)都派生自 System.Delegate 類。使用一個委託有三個步驟: 

定義委託
例項化委託
將指定的方法新增到委託物件中
例子:
        delegate int plus(int x, int y); // 1. 定義委託
 
        static void Main(string[] args)
        {
            plus del_p;  // 2. 例項化委託
 
            del_p = new plus(addition);  // 3. 將方法新增到例項化委託物件中
 
            int n = del_p(1, 2);
            Console.Write(n);
        }
 
        static int addition(int x, int y)
        {
            return x + y;  
        }

C#允許直接把方法名賦給委託,所以下面的三種寫法也正確:

            plus del_p = new plus(addition);
            plus del_p2 = addition;
            del_p = addition; 

委託完全可以被當做普通型別對待,比如可以加減、賦值、構建陣列。
委託陣列
委託可以構建陣列,意味著一組同樣返回型別和引數型別的方法。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc[] fs = new MathFunc[]
            {
                substract, 
                addition, 
                product,
                division
            };
 
            int n = fs[0](1, 2); // -1
        }
 
        static int division( int x, int y)
        {
            return x / y;
        }
 
        static int substract(int x, int y)
        {
            return x - y; 
        }
 
        static int addition(int x, int y)
        {
            return x + y;  
        }
 
        static int product(int x, int y)
        {
            return x * y;
        }

委託的加減法/委託的多播(Multicasting of a Delegate)
一個委託物件可以封裝多個方法,通過委託物件的合併(加法)實現。被合併的方法必須有相同的返回型別和引數型別。使用合併後的委託時,會依次將實參傳入被合併的方法,最後的返回值以最後一個方法為準。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f;
            f = addition;
            f += substract;
            f += product;
            int n = f(1, 2); // 2
        }
 
        static int division( int x, int y)
        {
            Console.WriteLine("division");
            return x / y;
        }
 
        static int substract(int x, int y)
        {
            Console.WriteLine("substraction");
            return x - y; 
        }
 
        static int addition(int x, int y)
        {
            Console.WriteLine("addition");
            return x + y;  
        }
 
        static int product(int x, int y)
        {
            Console.WriteLine("product");
            return x * y;
        }
上面的程式碼會打印出:

addition 
substraction 
product 

委託減法是加法的逆運算。如果被減委託中不含有減數委託,則不會對被減數造成任何影響,被減數減去自身為null,呼叫空委託會引發異常。總結下來委託加減法的幾個結論:
委託加減null沒有任何效果
委託減自身為null
減數如果沒有包含在被減數中,則沒有任何效果
在使用委託之前,應該判斷委託是否為null。

傳遞委託
委託可以被作為以下方式傳遞
變數
方法引數
方法返回值
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f = getAddFunc();
            int n = f(2, 3); // 5
            int n2 = add(f, 3, 4); // 7
        }
 
        static int add(MathFunc f, int x, int y)
        {
            return f(x,  y);
        }
 
        static MathFunc getAddFunc()
        {
            return addition;
            // or return new MathFunc(addition);
        }

匿名方法(delegate方法)
使用 delegate 關鍵字直接定義方法,和例項化委託不同,不使用 new 關鍵字來建立委託物件,而是直接定義方法引數和方法體。匿名方法並不是真的沒有名稱,而是指程式設計師無需命名。C#在編譯時會給方法生成一個方法定義(包含方法名和引數列表),該方法實際上是當前型別的一個私有靜態方法。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f = delegate (int x, int y)
            {
                return x + y;
            };
            int n = f(1, 2); // 3
        }
Lambda表示式
從C# 3.0 開始,可以用 lambda表示式替換匿名方法表示式,直接將一個 Lambda表示式賦給委託物件。注意Lambda表示式不能作為語句,必須為右值。Lambda表示式的引數和返回型別必須和委託一致。
        delegate int MathFunc(int x, int y); 
 
        static void Main(string[] args)
        {
            MathFunc f;
            f = (int x, int y) =>
            {
                return x + y;
            };
            int n = f(1, 2); // 3
        }
如果lambda表示式只有一個引數,則可以省略圓括號。Lambda表示式允許多種寫法,以下寫法等價:
            something = (int x, int y) => { return x + y; };
            something = (int x, int y) => x + y;
            something = (x, y) => x + y;
            something = (x, y) => { return x + y; }; 
Lambda表示式中沒有用到的引數可以用 _ 代替。比如
something = (x, _) => x * 2; 

在Lambda表示式和匿名delegate方法內部都可以訪問主調函式的外部變數,比如:
            MathFunc f;
            string str = "str";
            f = (x, y) =>
            {
                Console.Write(str); // 訪問了外部的str
                return x + y;
            };
如果Lambda表示式或者匿名delegate方法的引數名和外部變數衝突,則無法通過編譯。
委託釋出和訂閱
基於委託多播的特性,可以實現 “釋出者/訂閱者”模式。釋出者物件有一個委託成員,儲存一系列別的類的例項(訂閱者)的方法,一旦釋出者觸發(呼叫)了委託,就會把所有註冊過的方法都依次呼叫依次,通知訂閱者。比如有一個紅綠燈,一旦紅綠燈變色,就會通知所有車輛和行人,此時可以把車輛和行人的響應方法註冊到紅綠燈物件的一個 public 委託成員 OnColorChange 裡,當紅綠燈呼叫 OnColorChange 時,就會通知所有車輛,呼叫他們的響應函式。由於訂閱者的型別和響應方法千奇百怪,可以高度定製針對一個事件(比如紅綠燈變色)的不同物件的不同響應函式。
由於委託成員 OnColorChange 是公有的,一旦別的程式將委託置為空,或其他值,委託先前的修改就前功盡棄了。為了防止這種事情發生,C# 提供了 event 來修飾(宣告)委託成員。經過 event 修飾後,委託成員只能被 += 和 -= 修改,而不能用 = 修改。
--------------------- 
作者:csdn_chai 
來源:CSDN 
原文:https://blog.csdn.net/csdn_chai/article/details/77429538 
版權宣告:本文為博主原創文章,轉載請附上博文連結!