標籤:匿名方法 enter number 回調 連結 int c# 自動更新 如何
本篇已收錄至《C#圖解教程》讀書筆記目錄貼,點擊訪問該目錄可擷取更多內容。
一、委託初窺:一個擁有方法的對象
(1)本質:持有一個或多個方法的對象;委託和典型的對象不同,執行委託實際上是執行它所“持有”的方法。如果從C++的角度來理解委託,可以將其理解為一個型別安全的、物件導向的函數指標。
(2)如何使用委託?
①聲明委託類型(delegate關鍵字)
②使用該委託型別宣告一個委託變數
③為委託類型增加方法
④調用委託執行方法
(3)委託的恒定性:
組合委託、為委託+=增加方法以及為委託-=移除方法讓我們看起來像是委託被修改了,其實它們並沒有被修改。事實上,委託是恒定的。
在為委託增加和移除方法時實際發生的是建立了一個新的委託,其調用列表是增加和移除後的方法結果。
(4)委託執行個體:
①簡單帶參數委託DEMO
delegate void MyDel(int value); //聲明委託類型 class Program { void PrintLow(int value) { Console.WriteLine("{0} - LowValue", value); } void PrintHigh(int value) { Console.WriteLine("{0} - HighValue", value); } static void Main(string[] args) { Program program = new Program(); MyDel myDel; //聲明委託類型 //擷取0~99之間的一個隨機數 Random random = new Random(); int randomValue = random.Next(99); //建立一個包含具體方法的委派物件並將其賦值給myDel變數 myDel = randomValue < 50 ? new MyDel(program.PrintLow) : new MyDel(program.PrintHigh); //執行委託 myDel(randomValue); Console.ReadKey(); } }
②簡單無參數多方法列表委託DEMO
delegate void PrintFunction();class Test{ public void Print1() { Console.WriteLine( "Print1 -- instance" ); } public static void Print2() { Console.WriteLine( "Print2 -- static" ); }}class Program{ static void Main() { Test t = new Test(); PrintFunction pf; pf = t.Print1; pf += Test.Print2; pf += t.Print1; pf += Test.Print2; if ( pf != null ) { pf(); } else { Console.WriteLine( "Delegate is empty" ); } }}
③帶傳回值的委託DEMO
delegate int MyDel(); class MyClass{ int IntValue = 5; public int Add2() { IntValue += 2; return IntValue; } public int Add3() { IntValue += 3; return IntValue; }}class Program{ static void Main() { MyClass mc = new MyClass(); MyDel mDel = mc.Add2; mDel += mc.Add3; mDel += mc.Add2; Console.WriteLine( "Value: {0}", mDel() ); }}二、匿名方法:不好意思,我匿了
在委託所持有的方法中,如果某個方法只被使用一次,這種情況下,除了建立委託文法的需要,沒有必要建立獨立的具名方法。因此,匿名方法應運而生。
匿名方法是在初始化委託時內聯(inline)聲明的方法。
下面來看看在兩個版本的代碼:具名方法和匿名方法的比較,匿名方法是不是簡潔得多?
①具名參數
View Code
②匿名參數
View Code三、Lambda運算式:好吃的文法糖
(1)本質:簡化文法的”文法糖“;
Lambda來源:1920年到1930年期間,數學家Alonzo Church等人發明了Lambda積分。Lambda積分是用於表示函數的一套系統,它使用希臘字母Lambda(λ)來表示無名函數。近年來,函數式程式設計語言(如Lisp)使用這個術語來表示可以直接描述函數定義的運算式,運算式不再需要有名字了。
(2)要點:
①Lambda運算式中的參數列表(參數數量、類型和位置)必須與委託相匹配;
②運算式中的參數列表不一定需要包含類型,除非委託有ref或out關鍵字(此時必須顯示聲明);
③如果沒有參數,必須使用一組空的圓括弧;
(3)文法:
四、事件初窺:發行者和訂閱者模式
發行者訂閱者模式定義了一種一對多的依賴關係,讓多個訂閱者對象同時監聽某一個主題對象。這個主題對象在自身狀態變化時,會通知所有訂閱者對象,使它們能夠自動更新自己的狀態。
由訂閱者提供的方法稱為回調方法,因為發行者通過執行這些方法來”往回調用訂閱者的方法“。還可以將它們稱為事件處理常式,因為它們是為處理事件而調用的代碼。
下面通過一段經典的代碼來看看這個模式的應用:
using System;delegate void Handler();class Incrementer{ public event Handler CountedADozen; public void DoCount() { for ( int i=1; i < 100; i++ ) if ( i % 12 == 0 && CountedADozen != null ) CountedADozen(); }}class Dozens{ public int DozensCount { get; private set; } public Dozens( Incrementer incrementer ) { DozensCount = 0; incrementer.CountedADozen += IncrementDozensCount; } void IncrementDozensCount() { DozensCount++; }}class Program{ static void Main() { Incrementer incrementer = new Incrementer(); Dozens dozensCounter = new Dozens( incrementer ); incrementer.DoCount(); Console.WriteLine( "Number of dozens = {0}", dozensCounter.DozensCount ); }}五、事件全過程:聲明、訂閱和觸發
(1)聲明事件:
①事件聲明在一個類中;
②附加的方法需與委託類型的簽名和傳回型別匹配;
③聲明為public;
④無法new;
(2)訂閱事件:
①使用+=為事件增加事件處理常式;
②可以使用匿名方法和Lambda運算式;
(3)觸發事件:
①使用事件名稱,後面跟的參數列表包含在圓括弧中;
②參數列表必須與事件的委託類型相匹配;
六、走向標準之路:EventHandler
程式的非同步處理是使用C#事件的絕佳情境。Windows GUI廣泛地使用了事件,對於事件的使用,.NET架構提供了一個標準模式:EventHandler委託類型。
(1)第一個參數儲存觸發事件的對象的引用(object類型,可以匹配任何類型的執行個體);
(2)第二個參數儲存狀態資訊(EventArgs類的執行個體),指明什麼程式適用於該應用程式;
(3)傳回型別為void;
現在我們來重構剛剛的訂閱者類,使用標準的EventHandler委託類型:
class Dozens{ public int DozensCount { get; private set; } public Dozens( Incrementer incrementer ) { DozensCount = 0; incrementer.CountedADozen += IncrementDozensCount; } void IncrementDozensCount( object source, EventArgs e ) { DozensCount++; }}
那麼,剛剛看到為了保持標準模式,我們只能有兩個參數,第一個是觸發事件的對象引用,第二個是EventArgs類的執行個體,如何在事件中傳遞資料呢?答案肯定是在第二個參數上找到切入點。我們可以聲明一個派生自EventArgs的子類,在其中聲明我們要傳遞的參數所對應的屬性來儲存我們需要傳入的資料。TIPS:這個自訂子類的名稱建議以EventArgs結尾。
public class IncrementerEventArgs : EventArgs{ public int IterationCount { get; set; } }
既然使用了自訂類,那麼在事件的其他幾部分中要使用該自訂類還必須改為泛型委派和聲明自訂類對象。
class Incrementer{ public event EventHandler<IncrementerEventArgs> CountedADozen; public void DoCount() { IncrementerEventArgs args = new IncrementerEventArgs(); for ( int i=1; i < 100; i++ ) if ( i % 12 == 0 && CountedADozen != null ) { args.IterationCount = i; CountedADozen( this, args ); } }}
為了在執行程式中擷取到傳遞的資料值,便可以直接通過派生自EventArgs的自訂類的屬性的到。
class Dozens{ public int DozensCount { get; private set; } public Dozens( Incrementer incrementer ) { DozensCount = 0; incrementer.CountedADozen += IncrementDozensCount; } void IncrementDozensCount( object source, IncrementerEventArgs e ) { Console.WriteLine( "Incremented at iteration: {0} in {1}", e.IterationCount, source.ToString() ); DozensCount++; }}本章思維導圖
附件
思維導圖(jpg、pdf以及mmap源檔案)下載:http://pan.baidu.com/s/1hqA7KH2
周旭龍
出處:http://www.cnblogs.com/edisonchou/
本文著作權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。
《C#圖解教程》讀書筆記之五:委託和事件