<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      C#綜合揭秘——深入分析委托與事件

      引言

      本篇文章將為你介紹一下 Delegate 的使用方式,逐漸揭開 C# 當中事件(Event)的由來,它能使處理委托類型的過程變得更加簡單。
      還將為您解釋委托的協變與逆變,以及如何使用 Delegate 使 Observer(觀察者)模式的使用變得更加簡單。
      在事件的介紹上,會講述事件的使用方式,并以ASP.NET的用戶控件為例子,介紹一下自定義事件的使用。
      最后一節,將介紹Predicate<T>、Action<T>、Func<T,TResult>多種泛型委托的使用和Lambda的發展過程與其使用方式。
      因為時間倉促,文中有錯誤的地方敬請點評。

       

       

      目錄

      一、委托類型的來由

      二、建立委托類

      三、委托使用方式

      四、深入解析事件

      五、Lambda 表達式

       

       

       

      一、委托類型的來由

      記得在使用C語言的年代,整個項目中都充滿著針指的身影,那時候流行使用函數指針來創建回調函數,使用回調可以把函數回調給程序中的另一個函數。但函數指針只是簡單地把地址指向另一個函數,并不能傳遞其他額外信息。
      在.NET中,在大部分時間里都沒有指針的身影,因為指針被封閉在內部函數當中。可是回調函數卻依然存在,它是以委托的方式來完成的。委托可以被視為一個更高級的指針,它不僅僅能把地址指向另一個函數,而且還能傳遞參數,返回值等多個信息。系統還為委托對象自動生成了同步、異步的調用方式,開發人員使用 BeginInvoke、EndInvoke 方法就可以拋開 Thread 而直接使用多線程調用 。

      回到目錄

       

      二、建立委托類

      使用delegate就可以直接建立任何名稱的委托類型,當進行系統編譯時,系統就會自動生成此類型。您可以使用delegate void MyDelegate() 方式建立一個委托類,并使用ILDASM.exe觀察其成員。由ILDASM.exe 中可以看到,它繼承了System.MulticastDelegate類,并自動生成BeginInvoke、EndInvoke、Invoke 等三個常用方法。

      Invoke 方法是用于同步調用委托對象的對應方法,而BeginInvoke、EndInvoke是用于以異步方式調用對應方法的。
      對于異步調用的使用方式,可以參考:C#綜合揭秘——細說多線程

      1      public class MyDelegate:MulticastDelegate
      2 {
      3 //同步調用委托方法
      4 public virtual void Invoke();
      5 //異步調用委托方法
      6 public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);
      7 public virtual void EndInvoke(IAsyncResult result);
      8 }

      MulticastDelegate是System.Delegate的子類,它是一個特殊類,編譯器和其他工具可以從此類派生,但是自定義類不能顯式地從此類進行派生。它支持多路廣播委托,并擁有一個帶有鏈接的委托列表,在調用多路廣播委托時,系統將按照調用列表中的委托出現順序來同步調用這些委托。

      MulticastDelegate具有兩個常用屬性:Method、Target。其中Method 用于獲取委托所表示的方法Target 用于獲取當前調用的類實例。

      MulticastDelegate有以下幾個常用方法:

      方法名稱說明
       Clone   創建委托的淺表副本。
       GetInvocationList   按照調用順序返回此多路廣播委托的調用列表。
       GetMethodImpl   返回由當前的 MulticastDelegate 表示的靜態方法。
       GetObjectData   用序列化該實例所需的所有數據填充 SerializationInfo 對象。
       MemberwiseClone   創建當前 Object 的淺表副本。
       RemoveImpl   調用列表中移除與指定委托相等的元素

      MulticastDelegate與Delegate給委托對象建立了強大的支持,下面向各位詳細介紹一下委托的使用方式。

      回到目錄

       

      三、委托使用方式

      3.1 簡單的委托

      當建立委托對象時,委托的參數類型必須與委托方法相對應。只要向建立委托對象的構造函數中輸入方法名稱example.Method,委托就會直接綁定此方法。使用myDelegate.Invoke(string message),就能顯式調用委托方法。但在實際的操作中,我們無須用到 Invoke 方法,而只要直接使用myDelegate(string message),就能調用委托方法。

       1     class Program
      2 {
      3 delegate void MyDelegate(string message);
      4
      5 public class Example
      6 {
      7 public void Method(string message)
      8 {
      9 MessageBox.Show(message);
      10 }
      11 }
      12
      13 static void Main(string[] args)
      14 {
      15 Example example=new Example();
      16 MyDelegate myDelegate=new MyDelegate(example.Method);
      17 myDelegate("Hello World");
      18 Console.ReadKey();
      19 }
      20 }

       

      3.2 帶返回值的委托

      當建立委托對象時,委托的返回值必須與委托方法相對應。使用下面的例子,方法將返回 “Hello Leslie” 。

       1     class Program
      2 {
      3 delegate string MyDelegate(string message);
      4
      5 public class Example
      6 {
      7 public string Method(string name)
      8 {
      9 return "Hello " + name;
      10 }
      11 }
      12
      13 static void Main(string[] args)
      14 {
      15 Example example=new Example();
      16 //綁定委托方法
      17 MyDelegate myDelegate=new MyDelegate(example.Method);
      18 //調用委托,獲取返回值
      19 string message = myDelegate("Leslie");
      20 Console.WriteLine(message);
      21 Console.ReadKey();
      22 }
      23 }

       

      3.3 多路廣播委托

      在第二節前曾經提過,委托類繼承于MulticastDelegate,這使委托對象支持多路廣播,即委托對象可以綁定多個方法。當輸入參數后,每個方法會按順序進行迭代處理,并返回最后一個方法的計算結果。
      下面的例子中,Price 類中有兩個計算方法,Ordinary 按普通的9.5折計算,Favourable 按優惠價 8.5 折計算。委托同時綁定了這兩個方法,在輸入參數100以后,Ordinary、Favourable這兩個方法將按順序迭代執行下去,最后返回 Favourable 方法的計算結果 85。

       1         delegate double MyDelegate(double message);
      2
      3 public class Price
      4 {
      5 public double Ordinary(double price)
      6 {
      7 double price1 = 0.95 * price;
      8 Console.WriteLine("Ordinary Price : "+price1);
      9 return price1;
      10 }
      11
      12 public double Favourable(double price)
      13 {
      14 double price1 = 0.85 * price;
      15 Console.WriteLine("Favourable Price : " + price1);
      16 return price1;
      17 }
      18
      19 static void Main(string[] args)
      20 {
      21 Price price = new Price();
      22 //綁定Ordinary方法
      23 MyDelegate myDelegate = new MyDelegate(price.Ordinary);
      24 //綁定Favourable方法
      25 myDelegate += new MyDelegate(price.Favourable);
      26 //調用委托
      27 Console.WriteLine("Current Price : " + myDelegate(100));
      28 Console.ReadKey();
      29 }
      30 }

      運行結果


      3.4 淺談Observer模式

      回顧一下簡單的 Observer 模式,它使用一對多的方式,可以讓多個觀察者同時關注同一個事物,并作出不同的響應。
      例如下面的例子,Manager的底薪為基本工資的1.5倍,Assistant的底薪為基本工資的1.2倍。WageManager類的RegisterWorker方法與RemoveWorker方法可以用于注冊和注銷觀察者,最后執行Execute方法可以對多個已注冊的觀察者同時輸入參數。

       

       

       1     public class WageManager
      2 {
      3 IList<Worker> workerList = new List<Worker>();
      4
      5 public void RegisterWorker(Worker worker)
      6 {
      7 workerList.Add(worker);
      8 }
      9
      10 public void RemoveWorker(Worker worker)
      11 {
      12 workerList.Remove(worker);
      13 }
      14
      15 public void Excute(double basicWages)
      16 {
      17 if (workerList.Count != 0)
      18 foreach (var worker in workerList)
      19 worker.GetWages(basicWages);
      20 }
      21
      22 static void Main(string[] args)
      23 {
      24 WageManager wageManager = new WageManager();
      25 //注冊觀察者
      26 wageManager.RegisterWorker(new Manager());
      27 wageManager.RegisterWorker(new Assistant());
      28 //同時輸入底薪3000元,分別進行計算
      29 wageManager.Excute(3000);
      30
      31 Console.ReadKey();
      32 }
      33 }
      34
      35 public abstract class Worker
      36 {
      37 public abstract double GetWages(double basicWages);
      38 }
      39
      40 public class Manager:Worker
      41 {
      42 //Manager實際工資為底薪1.5倍
      43 public override double GetWages(double basicWages)
      44 {
      45 double totalWages = 1.5 * basicWages;
      46 Console.WriteLine("Manager's wages is " + totalWages);
      47 return totalWages;
      48 }
      49 }
      50
      51 public class Assistant : Worker
      52 {
      53 //Assistant實際工資為底薪的1.2倍
      54 public override double GetWages(double basicWages)
      55 {
      56 double totalWages = 1.2 * basicWages;
      57 Console.WriteLine("Assistant's wages is " + totalWages);
      58 return totalWages;
      59 }
      60 }

      運行結果

       

      開發 Observer 模式時借助委托,可以進一步簡化開發的過程。由于委托對象支持多路廣播,所以可以把Worker類省略。在WageManager類中建立了一個委托對象wageHandler,通過Attach與Detach方法可以分別加入或取消委托。如果觀察者想對事物進行監測,只需要加入一個委托對象即可。記得在第二節曾經提過,委托的GetInvodationList方法能獲取多路廣播委托列表,在Execute方法中,就是通過去多路廣播委托列表去判斷所綁定的委托數量是否為0。

       1         public delegate double Handler(double basicWages);
      2
      3 public class Manager
      4 {
      5 public double GetWages(double basicWages)
      6 {
      7 double totalWages=1.5 * basicWages;
      8 Console.WriteLine("Manager's wages is : " + totalWages);
      9 return totalWages;
      10 }
      11 }
      12
      13 public class Assistant
      14 {
      15 public double GetWages(double basicWages)
      16 {
      17 double totalWages = 1.2 * basicWages;
      18 Console.WriteLine("Assistant's wages is : " + totalWages);
      19 return totalWages;
      20 }
      21 }
      22
      23 public class WageManager
      24 {
      25 private Handler wageHandler;
      26
      27 //加入觀察者
      28 public void Attach(Handler wageHandler1)
      29 {
      30 wageHandler += wageHandler1;
      31 }
      32
      33 //刪除觀察者
      34 public void Detach(Handler wageHandler1)
      35 {
      36 wageHandler -= wageHandler1;
      37 }
      38
      39 //通過GetInvodationList方法獲取多路廣播委托列表,如果觀察者數量大于0即執行方法
      40 public void Execute(double basicWages)
      41 {
      42 if (wageHandler!=null)
      43 if(wageHandler.GetInvocationList().Count() != 0)
      44 wageHandler(basicWages);
      45 }
      46
      47 static void Main(string[] args)
      48 {
      49 WageManager wageManager = new WageManager();
      50 //加入Manager觀察者
      51 Manager manager = new Manager();
      52 Handler managerHandler = new Handler(manager.GetWages);
      53 wageManager.Attach(managerHandler);
      54
      55 //加入Assistant觀察者
      56 Assistant assistant = new Assistant();
      57 Handler assistantHandler = new Handler(assistant.GetWages);
      58 wageManager.Attach(assistantHandler);
      59
      60 //同時加入底薪3000元,分別進行計算
      61 wageManager.Execute(3000);
      62 Console.ReadKey();
      63 }
      64 }

      最后運行結果與上面的例子相同。

       

      3.5 委托的協變與逆變

      在 Framework 2.0 出現之前,委托協變這個概念還沒有出現。此時因為委托是安全類型,它們不遵守繼承的基礎規則。即會這下面的情況:Manager 雖然是 Worker 的子類,但 GetWorkerHander 委托不能直接綁定 GetManager 方法,因為在委托當中它們的返回值 Manager 與 Worker 被視為完全無關的兩個類型。

       1      public class Worker
      2 {.......}
      3 public class Manager:Worker
      4 {.......}
      5
      6 class Program
      7 {
      8 public delegate Worker GetWorkerHandler(int id);
      9 public delegate Manager GetManagerHandler(int id);
      10
      11 public static Worker GetWorker(int id)
      12 {
      13 Worker worker = new Worker();
      14 ..............
      15 return worker;
      16 }
      17
      18 public static Manager GetManager(int id)
      19 {
      20 Manager manager = new Manager();
      21 ..............
      22 return manager;
      23 }
      24
      25 static void Main(string[] args)
      26 {
      27 GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker);
      28 var worker=workerHandler(1);
      29
      30 GetManagerHandler managerHandler = new GetManagerHandler(GetManager);
      31 var manager = managerHandler(2);
      32 Console.ReadKey();
      33 }
      34 }

      自從Framework 2.0 面試以后,委托協變的概念就應運而生,此時委托可以按照傳統的繼承規則進行轉換。即 GetWorkerHandler 委托可以直接綁定 GetManager 方法。

       1      public class Worker
      2 {.......}
      3 public class Manager:Worker
      4 {.......}
      5
      6 class Program
      7 {
      8 public delegate Worker GetWorkerHandler(int id);
      9 //在 Framework2.0 以上,委托 GetWorkerHandler 可綁定 GetWorker 與 GetManager 兩個方法
      10
      11 public static Worker GetWorker(int id)
      12 {
      13 Worker worker = new Worker();
      14 return worker;
      15 }
      16
      17 public static Manager GetManager(int id)
      18 {
      19 Manager manager = new Manager();
      20 return manager;
      21 }
      22
      23 static void Main(string[] args)
      24 {
      25 GetWorkerHandler workerHandler = new GetWorkerHandler(GetWorker);
      26 Worker worker=workerHandler(1);
      27 GetWorkerHandler managerHandler = new GetWorkerHandler(GetManager);
      28 Manager manager = managerHandler(2) as Manager;
      29 Console.ReadKey();
      30 }
      31 }

      委托逆變,是指委托方法的參數同樣可以接收 “繼承” 這個傳統規則。像下面的例子,以 object 為參數的委托,可以接受任何 object 子類的對象作為參數。最后可以在處理方法中使用 is 對輸入數據的類型進行判斷,分別處理對不同的類型的對象。

       1     class Program
      2 {
      3 public delegate void Handler(object obj);
      4
      5 public static void GetMessage(object message)
      6 {
      7 if (message is string)
      8 Console.WriteLine("His name is : " + message.ToString());
      9 if (message is int)
      10 Console.WriteLine("His age is : " + message.ToString());
      11 }
      12
      13 static void Main(string[] args)
      14 {
      15 Handler handler = new Handler(GetMessage);
      16 handler(29);
      17 Console.ReadKey();
      18 }
      19 }

      運行結果

      注意委托與其綁定方法的參數必須一至,即當 Handler 所輸入的參數為 A 類型,其綁定方法 GetMessage 的參數也必須為 A 類或者 A 的父類 。相反,當綁定方法的參數為 A 的子類,系統也無法辨認。

       
      3.6 泛型委托

      委托逆變雖然實用,但如果都以 object 作為參數,則需要每次都對參數進行類型的判斷,這不禁令人感到厭煩。
      為此,泛型委托應運而生,泛型委托有著委托逆變的優點,同時利用泛型的特性,可以使一個委托綁定多個不同類型參數的方法,而且在方法中不需要使用 is 進行類型判斷,從而簡化了代碼。

       1     class Program
      2 {
      3 public delegate void Handler<T>(T obj);
      4
      5 public static void GetWorkerWages(Worker worker)
      6 {
      7 Console.WriteLine("Worker's total wages is " + worker.Wages);
      8 }
      9
      10 public static void GetManagerWages(Manager manager)
      11 {
      12 Console.WriteLine("Manager's total wages is "+manager.Wages);
      13 }
      14
      15 static void Main(string[] args)
      16 {
      17 Handler<Worker> workerHander = new Handler<Worker>(GetWorkerWages);
      18 Worker worker = new Worker();
      19 worker.Wages = 3000;
      20 workerHander(worker);
      21
      22 Handler<Manager> managerHandler = new Handler<Manager>(GetManagerWages);
      23 Manager manager = new Manager();
      24 manager.Wages = 4500;
      25 managerHandler(manager);
      26
      27 Console.ReadKey();
      28 }
      29 }

      運行結果

      回到目錄

      四、深入解析事件

      4.1 事件的由來

      在介紹事件之前大家可以先看看下面的例子, PriceManager 負責對商品價格進行處理,當委托對象 GetPriceHandler 的返回值大于100元,按8.8折計算,低于100元按原價計算。

       1     public delegate double PriceHandler();
      2
      3 public class PriceManager
      4 {
      5 public PriceHandler GetPriceHandler;
      6
      7 //委托處理,當價格高于100元按8.8折計算,其他按原價計算
      8 public double GetPrice()
      9 {
      10 if (GetPriceHandler.GetInvocationList().Count() > 0)
      11 {
      12 if (GetPriceHandler() > 100)
      13 return GetPriceHandler()*0.88;
      14 else
      15 return GetPriceHandler();
      16 }
      17 return -1;
      18 }
      19 }
      20
      21 class Program
      22 {
      23 static void Main(string[] args)
      24 {
      25 PriceManager priceManager = new PriceManager();
      26
      27 //調用priceManager的GetPrice方法獲取價格
      28 //直接調用委托的Invoke獲取價格,兩者進行比較
      29 priceManager.GetPriceHandler = new PriceHandler(ComputerPrice);
      30 Console.WriteLine(string.Format("GetPrice\n Computer's price is {0}!",
      31 priceManager.GetPrice()));
      32 Console.WriteLine(string.Format("Invoke\n Computer's price is {0}!",
      33 priceManager.GetPriceHandler.Invoke()));
      34
      35 Console.WriteLine();
      36
      37 priceManager.GetPriceHandler = new PriceHandler(BookPrice);
      38 Console.WriteLine(string.Format("GetPrice\n Book's price is {0}!",
      39 priceManager.GetPrice()));
      40 Console.WriteLine(string.Format("Invoke\n Book's price is {0}!" ,
      41 priceManager.GetPriceHandler.Invoke()));
      42
      43 Console.ReadKey();
      44 }
      45 //書本價格為98元
      46 public static double BookPrice()
      47 {
      48 return 98.0;
      49 }
      50 //計算機價格為8800元
      51 public static double ComputerPrice()
      52 {
      53 return 8800.0;
      54 }
      55 }

      運行結果

      觀察運行的結果,如果把委托對象 GetPriceHandler 設置為 public ,外界可以直接調用 GetPriceHandler.Invoke 獲取運行結果而移除了 GetPrice 方法的處理,這正是開發人員最不想看到的。
      為了保證系統的封裝性,開發往往需要把委托對象 GetPriceHandler 設置為 private, 再分別加入 AddHandler,RemoveHandler 方法對 GetPriceHandler 委托對象進行封裝。

       1     public delegate double PriceHandler();
      2
      3 public class PriceManager
      4 {
      5 private PriceHandler GetPriceHandler;
      6
      7 //委托處理,當價格高于100元按8.8折計算,其他按原價計算
      8 public double GetPrice()
      9 {
      10 if (GetPriceHandler!=null)
      11 {
      12 if (GetPriceHandler() > 100)
      13 return GetPriceHandler()*0.88;
      14 else
      15 return GetPriceHandler();
      16 }
      17 return -1;
      18 }
      19
      20 public void AddHandler(PriceHandler handler)
      21 {
      22 GetPriceHandler += handler;
      23 }
      24
      25 public void RemoveHandler(PriceHandler handler)
      26 {
      27 GetPriceHandler -= handler;
      28 }
      29 }
      30 ................
      31 ................

      為了保存封裝性,很多操作都需要加入AddHandler、RemoveHandler 這些相似的方法代碼,這未免令人感到厭煩。
      為了進一步簡化操作,事件這個概念應運而生。

      4.2 事件的定義

      事件(event)可被視作為一種特別的委托,它為委托對象隱式地建立起add_XXX、remove_XXX 兩個方法,用作注冊與注銷事件的處理方法。而且事件對應的變量成員將會被視為 private 變量,外界無法超越事件所在對象直接訪問它們,這使事件具備良好的封裝性,而且免除了add_XXX、remove_XXX等繁瑣的代碼。

      1     public class EventTest
      2 {
      3 public delegate void MyDelegate();
      4 public event MyDelegate MyEvent;
      5 }

      觀察事件的編譯過程可知,在編譯的時候,系統為 MyEvent 事件自動建立add_MyEvent、remove_MyEvent 方法。

       

      4.3 事件的使用方式

      事件能通過+=和-=兩個方式注冊或者注銷對其處理的方法,使用+=與-=操作符的時候,系統會自動調用對應的 add_XXX、remove_XXX 進行處理。
      值得留意,在PersonManager類的Execute方法中,如果 MyEvent 綁定的處理方法不為空,即可使用MyEvent(string)引發事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 來引發事件,系統將引發錯誤報告。這正是因為事件具備了良好的封裝性,使外界不能超越事件所在的對象訪問其變量成員。

      注意在事件所處的對象之外,事件只能出現在+=,-=的左方。

       此時,開發人員無須手動添加 add_XXX、remove_XXX 的方法,就可實現與4.1例子中的相同功能,實現了良好的封裝。

       1     public delegate void MyDelegate(string name);
      2
      3 public class PersonManager
      4 {
      5 public event MyDelegate MyEvent;
      6
      7 //執行事件
      8 public void Execute(string name)
      9 {
      10 if (MyEvent != null)
      11 MyEvent(name);
      12 }
      13 }
      14
      15 class Program
      16 {
      17 static void Main(string[] args)
      18 {
      19 PersonManager personManager = new PersonManager();
      20 //綁定事件處理方法
      21 personManager.MyEvent += new MyDelegate(GetName);
      22 personManager.Execute("Leslie");
      23 Console.ReadKey();
      24 }
      25
      26 public static void GetName(string name)
      27 {
      28 Console.WriteLine("My name is " + name);
      29 }
      30 }

       

      4.4 事件處理方法的綁定

      在綁定事件處理方法的時候,事件出現在+=、-= 操作符的左邊,對應的委托對象出現在+=、-= 操作符的右邊。對應以上例子,事件提供了更簡單的綁定方式,只需要在+=、-= 操作符的右方寫上方法名稱,系統就能自動辯認。

       1     public delegate void MyDelegate(string name);
      2
      3 public class PersonManager
      4 {
      5 public event MyDelegate MyEvent;
      6 .........
      7 }
      8
      9 class Program
      10 {
      11 static void Main(string[] args)
      12 {
      13 PersonManager personManager = new PersonManager();
      14 //綁定事件處理方法
      15 personManager.MyEvent += GetName;
      16 .............
      17 }
      18
      19 public static void GetName(string name)
      20 {.........}
      21 }

      如果覺得編寫 GetName 方法過于麻煩,你還可以使用匿名方法綁定事件的處理。

       1     public delegate void MyDelegate(string name);
      2
      3 public class PersonManager
      4 {
      5 public event MyDelegate MyEvent;
      6
      7 //執行事件
      8 public void Execute(string name)
      9 {
      10 if (MyEvent != null)
      11 MyEvent(name);
      12 }
      13
      14 static void Main(string[] args)
      15 {
      16 PersonManager personManager = new PersonManager();
      17 //使用匿名方法綁定事件的處理
      18 personManager.MyEvent += delegate(string name){
      19 Console.WriteLine("My name is "+name);
      20 };
      21 personManager.Execute("Leslie");
      22 Console.ReadKey();
      23 }
      24 }

       

      4.5 C#控件中的事件

      在C#控件中存在多個的事件,像Click、TextChanged、SelectIndexChanged 等等,很多都是通過 EventHandler 委托綁定事件的處理方法的,EventHandler 可說是C#控件中最常見的委托 。

      public delegate void EventHandler (Object sender, EventArgs e)

      EventHandler 委托并無返回值,sender 代表引發事件的控件對象,e 代表由該事件生成的數據 。在ASP.NET中可以直接通過btn.Click+=new EventHandler(btn_onclick) 的方式為控件綁定處理方法。

       1 <html xmlns="http://www.w3.org/1999/xhtml">
      2 <head runat="server">
      3 <title></title>
      4 <script type="text/C#" runat="server">
      5 protected void Page_Load(object sender, EventArgs e)
      6 {
      7 btn.Click += new EventHandler(btn_onclick);
      8 }
      9
      10 public void btn_onclick(object obj, EventArgs e)
      11 {
      12 Button btn = (Button)obj;
      13 Response.Write(btn.Text);
      14 }
      15 </script>
      16 </head>
      17 <body>
      18 <form id="form1" runat="server">
      19 <div>
      20 <asp:Button ID="btn" runat="server" Text="Button"/>
      21 </div>
      22 </form>
      23 </body>
      24 </html>

      更多時候,只需要在頁面使用 OnClick=“btn_onclick" 方法,在編譯的時候系統就會自動對事件處理方法進行綁定。

       1 <html xmlns="http://www.w3.org/1999/xhtml">
      2 <head runat="server">
      3 <title></title>
      4 <script type="text/C#" runat="server">
      5 public void btn_onclick(object obj, EventArgs e)
      6 {
      7 Button btn = (Button)obj;
      8 Response.Write(btn.Text);
      9 }
      10 </script>
      11 </head>
      12 <body>
      13 <form id="form1" runat="server">
      14 <div>
      15 <asp:Button ID="btn" runat="server" Text="Button" OnClick="btn_onclick"/>
      16 </div>
      17 </form>
      18 </body>
      19 </html>

       

      EventHandler 只是 EventHandler<TEventArgs> 泛型委托的一個簡單例子。事實上,大家可以利用 EventHandler<TEventArgs> 構造出所需要的委托。

      public delegate void EventHandler<TEventArgs> (Object sender, TEventArgs e)

      在EventHandler<TEventArgs>中,sender代表事件源,e 代表派生自EventArgs類的事件參數。開發人員可以建立派生自EventArgs的類,從中加入需要使用到的事件參數,然后建立 EventHandler<TEventArgs> 委托。

      下面的例子中,先建立一個派生自EventArgs的類MyEventArgs作為事件參數,然后在EventManager中建立事件myEvent , 通過 Execute 方法可以激發事件。最后在測試中綁定 myEvent 的處理方法 ShowMessage,在ShowMessage顯示myEventArgs 的事件參數 Message。

       1     public class MyEventArgs : EventArgs
      2 {
      3 private string args;
      4
      5 public MyEventArgs(string message)
      6 {
      7 args = message;
      8 }
      9
      10 public string Message
      11 {
      12 get { return args; }
      13 set { args = value; }
      14 }
      15 }
      16
      17 public class EventManager
      18 {
      19 public event EventHandler<MyEventArgs> myEvent;
      20
      21 public void Execute(string message)
      22 {
      23 if (myEvent != null)
      24 myEvent(this, new MyEventArgs(message));
      25 }
      26 }
      27
      28 class Program
      29 {
      30 static void Main(string[] args)
      31 {
      32 EventManager eventManager = new EventManager();
      33 eventManager.myEvent += new EventHandler<MyEventArgs>(ShowMessage);
      34 eventManager.Execute("How are you!");
      35 Console.ReadKey();
      36 }
      37
      38 public static void ShowMessage(object obj,MyEventArgs e)
      39 {
      40 Console.WriteLine(e.Message);
      41 }
      42 }

      運行結果

       

      4.6 為用戶控件建立事件

      在ASP.NET開發中,頁面往往會出現很多類似的控件與代碼,開發人員可以通過用戶控件來避免重復的代碼。但往往同一個用戶控件,在不同的頁面中需要有不同的響應。此時為用戶控件建立事件,便可輕松地解決此問題。
      下面例子中,在用戶控件 MyControl 中建立存在一個GridView控件,GridView 控件通過 GetPersonList 方法獲取數據源。在用戶控件中還定義了 RowCommand 事件,在 GridView 的 GridView_RowCommand 方法中激發此事件。這樣,在頁面使用此控件時,開發人員就可以定義不同的方法處理 RowCommand 事件。

       1 public class Person
      2 {
      3 public int ID
      4 { get; set; }
      5 public string Name
      6 { get; set; }
      7 public int Age
      8 { get; set; }
      9 }
      10
      11 <!-- 用戶控件 -->
      12 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="MyControl" %>
      13 <script type="text/C#" runat="server">
      14 protected void Page_Load(object sender, EventArgs e)
      15 {
      16 GridView1.DataSource = GetPersonList();
      17 GridView1.DataBind();
      18 }
      19
      20 //綁定數據源
      21 protected IList<Person> GetPersonList()
      22 {
      23 IList<Person> list = new List<Person>();
      24 Person person1 = new Person();
      25 person1.ID = 1;
      26 person1.Name = "Leslie";
      27 person1.Age = 29;
      28 list.Add(person1);
      29 ...........
      30 return list;
      31 }
      32
      33 public event GridViewCommandEventHandler RowCommand;
      34
      35 protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
      36 {
      37 if (RowCommand != null)
      38 RowCommand(sender, e);
      39 }
      40 </script>
      41 <div>
      42 <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
      43 onrowcommand="GridView1_RowCommand">
      44 <Columns>
      45 <asp:BoundField DataField="ID" HeaderText="ID"/>
      46 <asp:BoundField DataField="Name" HeaderText="Name"/>
      47 <asp:BoundField DataField="Age" HeaderText="Age"/>
      48 <asp:ButtonField CommandName="Get" Text="Select"/>
      49 </Columns>
      50 </asp:GridView>
      51 </div>
      52
      53 <!-- 頁面代碼 -->
      54 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
      55 <%@ Register Src="~/MyControl.ascx" TagPrefix="ascx" TagName="myControl" %>
      56 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      57
      58 <html xmlns="http://www.w3.org/1999/xhtml">
      59 <head runat="server">
      60 <title></title>
      61 <script type="text/C#" runat="server">
      62 protected void myControl_RowCommand(object sender, GridViewCommandEventArgs e)
      63 {
      64 if (e.CommandName == "Get")
      65 {
      66 GridView gridView=(GridView)sender;
      67 int index = int.Parse(e.CommandArgument.ToString());
      68 label.Text=gridView.Rows[index].Cells[1].Text;
      69 }
      70 }
      71 </script>
      72 </head>
      73 <body>
      74 <form id="form1" runat="server">
      75 <div>
      76 <ascx:myControl ID="myControl" runat="server" OnRowCommand="myControl_RowCommand"></ascx:myControl>
      77 <br />
      78 Select Name : <asp:Label ID="label" runat="server"></asp:Label><br />
      79 </div>
      80 </form>
      81 </body>
      82 </html>

      運行結果

       

      使用控件已有的事件固然簡單,但它限制了傳送的參數類型,使開發人員無法傳送額外的自定義參數。在結構比較復雜的用戶控件中,使用已有的控件事件,顯然不夠方便,此時,您可以考慮為用戶控件建立自定義事件。
      首先用戶控件中包含訂單信息與訂單明細列表,首先定義一個事件參數 MyEventArgs,里面包含了訂單信息與一個 OrderItem 數組。然后建立用戶控件的委托MyDelegate 與對應的事件 MyEvent,在 Button 的 Click 事件中激發 MyEvent 自定義事件。這樣在頁面處理方法 myControl_Click 中就可以通過事件參數 MyEventArgs 獲取用戶控件中的屬性,計算訂單的總體價格。

        1 <!--   基礎類    -->
      2 public class OrderItem
      3 {
      4 public OrderItem(string id,string goods,double price,int count)
      5 {
      6 this.OrderItemID = id; //明細單ID
      7 this.Goods = goods; //商品名稱
      8 this.Price = price; //商品單價
      9 this.Count = count; //商品數量
      10 }
      11
      12 public string OrderItemID
      13 { get; set; }
      14 public string Goods
      15 { get; set; }
      16 public double Price
      17 { get; set; }
      18 public int Count
      19 { get; set; }
      20 }
      21
      22 /// 事件參數
      23 public class MyEventArgs:EventArgs
      24 {
      25 public MyEventArgs(string name,string address,string tel,
      26 string orderCode,IList<OrderItem> orderItemList)
      27 {
      28 Name = name; //買家姓名
      29 Address = address; //買家地址
      30 Tel = tel; //買家電話
      31 OrderCode = orderCode; //訂單號碼
      32 OrderItemList = orderItemList; //訂單明細
      33 }
      34
      35 public string Name
      36 { get;set; }
      37 public string Address
      38 { get; set; }
      39 public string Tel
      40 { get; set; }
      41 public string OrderCode
      42 { get; set; }
      43 public IList<OrderItem> OrderItemList
      44 { get; set; }
      45 }
      46
      47 <!-- 用戶控件 -->
      48 <%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyControl.ascx.cs" Inherits="MyControl" %>
      49 <script type="text/C#" runat="server">
      50 protected void Page_Load(object sender, EventArgs e)
      51 {
      52 GridView1.DataSource = GetList();
      53 GridView1.DataBind();
      54 }
      55
      56 //模擬數據源
      57 protected IList<OrderItem> GetList()
      58 {
      59 IList<OrderItem> list = new List<OrderItem>();
      60 OrderItem orderItem = new OrderItem("1", "Asus N75S", 8800, 2);
      61 list.Add(orderItem);
      62 ..........
      63 return list;
      64 }
      65
      66 //自定義委托
      67 public delegate void MyDelegate(object sender,MyEventArgs myEventArgs);
      68 //自定義事件
      69 public event MyDelegate MyEvent;
      70
      71 //按下Button時激發自定義事件
      72 protected void btn_click(object sender, EventArgs e)
      73 {
      74 if (MyEvent != null)
      75 {
      76 MyEventArgs myEventArgs = new MyEventArgs(labelName.Text, labelAddress.Text, labelTel.Text
      77 , labelOrderCode.Text, GetList());
      78 MyEvent(this,myEventArgs);
      79 }
      80 }
      81 </script>
      82 <div>
      83 Name : <asp:Label ID="labelName" runat="server">Leslie</asp:Label><br />
      84 Address : <asp:Label ID="labelAddress" runat="server">ZhongShan University 2A 501</asp:Label><br />
      85 Tel : <asp:Label ID="labelTel" runat="server">13660123456</asp:Label><br />
      86 Order Code : <asp:Label ID="labelOrderCode" runat="server">A12012031223B0030</asp:Label><br /><br />
      87 <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CellPadding="5">
      88 <Columns>
      89 <asp:BoundField DataField="OrderItemID" HeaderText="ID"/>
      90 <asp:BoundField DataField="Goods" HeaderText="Goods"/>
      91 <asp:BoundField DataField="Price" HeaderText="Price"/>
      92 <asp:BoundField DataField="Count" HeaderText="Count"/>
      93 </Columns>
      94 </asp:GridView>
      95 <br />
      96 <asp:Button ID="btn" runat="server" Text="Account" OnClick="btn_click"/>
      97 </div>
      98
      99 <!-- 頁面處理 -->
      100 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
      101 <%@ Register Src="~/MyControl.ascx" TagPrefix="ascx" TagName="myControl" %>
      102 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      103
      104 <html xmlns="http://www.w3.org/1999/xhtml">
      105 <head runat="server">
      106 <title></title>
      107 <script type="text/C#" runat="server">
      108 //在頁面定義用戶控件MyEvent事件的處理方法
      109 protected void myControl_Click(object sender,MyEventArgs e)
      110 {
      111 //計算訂單總體價格
      112 double totalPrice=0;
      113 IList<OrderItem> list=e.OrderItemList;
      114 foreach(OrderItem item in list)
      115 totalPrice+=item.Price*item.Count;
      116 //展示訂單號及總體費用
      117 labelOrderCode.Text = e.OrderCode;
      118 labelTotalPrice.Text = totalPrice.ToString();
      119 }
      120 </script>
      121 </head>
      122 <body>
      123 <form id="form1" runat="server">
      124 <div>
      125 <ascx:myControl ID="myControl" runat="server" OnMyEvent="myControl_Click"></ascx:myControl>
      126 <br />
      127 OrderCode : <asp:Label ID="labelOrderCode" runat="server"></asp:Label><br />
      128 TotalPrice : <asp:Label ID="labelTotalPrice" runat="server"></asp:Label>
      129 </div>
      130 </form>
      131 </body>
      132 </html>

      運行結果


      若對自定義事件不太熟悉的朋友很多時候會使用 UserControl.FindControl 的方式獲取用戶控件中的屬性,但當你深入了解自定義事件的開發過程以后,就能有效簡化開發的過程。


      回到目錄

      五、Lambda 表達式

      5.1 Lambda 的意義

      在Framework 2.0 以前,聲明委托的唯一方法是通過方法命名,從Framework 2.0 起,系統開始支持匿名方法。
      通過匿名方法,可以直接把一段代碼綁定給事件,因此減少了實例化委托所需的編碼系統開銷。
      而在 Framework 3.0 開始,Lambda 表達式開始逐漸取代了匿名方法,作為編寫內聯代碼的首選方式。總體來說,Lambda 表達式的作用是為了使用更簡單的方式來編寫匿名方法,徹底簡化委托的使用方式。

       

      5.2 回顧匿名方法的使用

      匿名方法的使用已經在4.4節簡單介紹過,在此回顧一下。
      使用下面的方式,可以通過匿名方法為Button的Click事件綁定處理方法。

      1         static void Main(string[] args)
      2 {
      3 Button btn = new Button();
      4 btn.Click+=delegate(object obj,EventArgs e){
      5 MessageBox.Show("Hello World !");
      6 };
      7 }

      總是使用 delegate(){......} 的方式建立匿名方法,令人不禁感覺郁悶。于是從Framework 3.0 起, Lambda 表達式開始出現。

       

      5.3 簡單介紹泛型委托

      在介紹 Lambda 表達式前,先介紹一下常用的幾個泛型委托。

       

      5.3.1 泛型委托 Predicate<T>

      早在Framework 2.0 的時候,微軟就為 List<T> 類添加了 Find、FindAll 、ForEach 等方法用作數據的查找。

      public T Find ( Predicate<T> match)
      public List<T> FindAll(Predicate<T>  match)

      在這些方法中存在一個Predicate <T> 表達式,它是一個返回bool的泛型委托,能接受一個任意類型的對象作為參數。

      public delegate bool Predicate<T>(T obj)

      在下面例子中,Predicate 委托綁定了參數為Person類的方法Match作為查詢條件,然后使用 FindAll 方法查找到合適條件的 List<Person> 集合。

       1     class Program
      2 {
      3 static void Main(string[] args)
      4 {
      5 List<Person> list = GetList();
      6 //綁定查詢條件
      7 Predicate<Person> predicate = new Predicate<Person>(Match);
      8 List<Person> result = list.FindAll(predicate);
      9 Console.WriteLine(“Person count is : ” + result.Count);
      10 Console.ReadKey();
      11 }
      12 //模擬源數據
      13 static List<Person> GetList()
      14 {
      15 var personList = new List<Person>();
      16 var person1 = new Person(1,"Leslie",29);
      17 personList.Add(person1);
      18 ........
      19 return personList;
      20 }
      21 //查詢條件
      22 static bool Match(Person person)
      23 {
      24 return person.Age <= 30;
      25 }
      26 }
      27
      28 public class Person
      29 {
      30 public Person(int id, string name, int age)
      31 {
      32 ID = id;
      33 Name = name;
      34 Age = age;
      35 }
      36
      37 public int ID
      38 { get; set; }
      39 public string Name
      40 { get; set; }
      41 public int Age
      42 { get; set; }
      43 }

       

      5.3.2 泛型委托 Action

      Action<T> 的使用方式與 Predicate<T> 相似,不同之處在于 Predicate<T> 返回值為 bool ,  Action<T> 的返回值為 void。
      Action 支持0~16個參數,可以按需求任意使用。

      public delegate void Action()
      public delegate void Action<T1>(T1 obj1)
      public delegate void Action<T1,T2> (T1 obj1, T2 obj2)
      public delegate void Action<T1,T2,T3> (T1 obj1, T2 obj2,T3 obj3)
      ............
      public delegate void Action<T1,T2,T3,......,T16> (T1 obj1, T2 obj2,T3 obj3,......,T16 obj16)

       1         static void Main(string[] args)
      2 {
      3 Action<string> action=ShowMessage;
      4 action("Hello World");
      5 Console.ReadKey();
      6 }
      7
      8 static void ShowMessage(string message)
      9 {
      10 MessageBox.Show(message);
      11 }

       

      5.3.3 泛型委托 Func

      委托 Func 與 Action 相似,同樣支持 0~16 個參數,不同之處在于Func 必須具有返回值

      public delegate TResult Func<TResult>()
      public delegate TResult Func<T1,TResult>(T1 obj1)
      public delegate TResult Func<T1,T2,TResult>(T1 obj1,T2 obj2)
      public delegate TResult Func<T1,T2,T3,TResult>(T1 obj1,T2 obj2,T3 obj3)
      ............
      public delegate TResult Func<T1,T2,T3,......,T16,TResult>(T1 obj1,T2 obj2,T3 obj3,......,T16 obj16)

       1         static void Main(string[] args)
      2 {
      3 Func<double, bool, double> func = Account;
      4 double result=func(1000, true);
      5 Console.WriteLine("Result is : "+result);
      6 Console.ReadKey();
      7 }
      8
      9 static double Account(double a,bool condition)
      10 {
      11 if (condition)
      12 return a * 1.5;
      13 else
      14 return a * 2;
      15 }

       

      5.4 揭開 Lambda 神秘的面紗

      Lambda 的表達式的編寫格式如下:

           x=> x * 1.5

      當中 “ => ” 是 Lambda 表達式的操作符,在左邊用作定義一個參數列表,右邊可以操作這些參數。

      例子一, 先把 int x 設置 1000,通過 Action 把表達式定義為 x=x+500 ,最后通過 Invoke 激發委托。

      1         static void Main(string[] args)
      2 {
      3 int x = 1000;
      4 Action action = () => x = x + 500;
      5 action.Invoke();
      6
      7 Console.WriteLine("Result is : " + x);
      8 Console.ReadKey();
      9 }


      例子二,通過 Action<int> 把表達式定義 x=x+500, 到最后輸入參數1000,得到的結果與例子一相同。
      注意,此處Lambda表達式定義的操作使用 { } 括弧包括在一起,里面可以包含一系列的操作。

       1         static void Main(string[] args)
      2 {
      3 Action<int> action = (x) =>
      4 {
      5 x = x + 500;
      6 Console.WriteLine("Result is : " + x);
      7 };
      8 action.Invoke(1000);
      9 Console.ReadKey();
      10 }

       

      例子三,定義一個Predicate<int>,當輸入值大約等于1000則返回 true , 否則返回 false。與5.3.1的例子相比,Predicate<T>的綁定不需要顯式建立一個方法,而是直接在Lambda表達式里完成,簡潔方便了不少。

       1         static void Main(string[] args)
      2 {
      3 Predicate<int> predicate = (x) =>
      4 {
      5 if (x >= 1000)
      6 return true;
      7 else
      8 return false;
      9 };
      10 bool result=predicate.Invoke(500);
      11 Console.ReadKey();
      12 }

       

      例子四,在計算商品的價格時,當商品重量超過30kg則打9折,其他按原價處理。此時可以使用Func<double,int,double>,參數1為商品原價,參數2為商品重量,最后返回值為 double 類型。

       1         static void Main(string[] args)
      2 {
      3 Func<double, int, double> func = (price, weight) =>
      4 {
      5 if (weight >= 30)
      6 return price * 0.9;
      7 else
      8 return price;
      9 };
      10 double totalPrice = func(200.0, 40);
      11 Console.ReadKey();
      12 }


      例子五,使用Lambda為Button定義Click事件的處理方法。與5.2的例子相比,使用Lambda比使用匿名方法更加簡單。

      1         static void Main(string[] args)
      2 {
      3 Button btn = new Button();
      4 btn.Click += (obj, e) =>
      5 {
      6 MessageBox.Show("Hello World!");
      7 };
      8 Console.ReadKey();
      9 }


      例子六,此處使用5.3.1的例子,在List<Person>的FindAll方法中直接使用Lambda表達式。
      相比之下,使用Lambda表達式,不需要定義Predicate<T>對象,也不需要顯式設定綁定方法,簡化了不工序。

       1      class Program
      2 {
      3 static void Main(string[] args)
      4 {
      5 List<Person> personList = GetList();
      6
      7 //查找年齡少于30年的人
      8 List<Person> result=personList.FindAll((person) => person.Age =< 30);
      9 Console.WriteLine("Person count is : " + result.Count);
      10 Console.ReadKey();
      11 }
      12
      13 //模擬源數據
      14 static List<Person> GetList()
      15 {
      16 var personList = new List<Person>();
      17 var person1 = new Person(1,"Leslie",29);
      18 personList.Add(person1);
      19 .......
      20 return personList;
      21 }
      22 }
      23
      24 public class Person
      25 {
      26 public Person(int id, string name, int age)
      27 {
      28 ID = id;
      29 Name = name;
      30 Age = age;
      31 }
      32
      33 public int ID
      34 { get; set; }
      35 public string Name
      36 { get; set; }
      37 public int Age
      38 { get; set; }
      39 }


      當在使用LINQ技術的時候,到處都會彌漫著 Lambda 的身影,此時更能體現 Lambda 的長處。
      但 LINQ 涉及到分部類,分部方法,IEnumerable<T>,迭代器等多方面的知識,這些已經超出本章的介紹范圍。
      通過這一節的介紹,希望能夠幫助大家更深入地了解 Lambda 的使用。

       回到目錄

      本章小結

      本章主要介紹了委托(Delegate)的使用,委托對象是一個派生自 System.MultcastDelegate 的類,它能通過 Invoke 方式進行同步調用,也可以通過 BeginInvoke,EndInvoke 方式實現異步調用。而事件(Event)屬于一種特殊的委托,它與委托類型同步使用,可以簡化的開發過程。
      最后,本文還介紹了匿名方法的使用方式,以及 Lambda 表達式的由來。
      對 .NET 開發有興趣的朋友歡迎加入QQ群:162338858 同探討 !

       

      C#綜合揭秘

      通過修改注冊表建立Windows自定義協議
      Entity Framework 并發處理詳解

      細說進程、應用程序域與上下文

      細說多線程(上)

      細說多線程(下)
      細說事務
      深入分析委托與事件

       

      作者:風塵浪子
      http://www.rzrgm.cn/leslies2/archive/2012/03/22/2389318.html

      原創作品,轉載時請注明作者及出處





      posted on 2012-03-22 11:04  風塵浪子  閱讀(72632)  評論(42)    收藏  舉報

      導航

      主站蜘蛛池模板: 思思久99久女女精品| av中文字幕在线二区| 亚洲国产在一区二区三区| 亚洲丶国产丶欧美一区二区三区| 亚洲精品网站在线观看不卡无广告 | 青青青视频免费一区二区| 亚洲精品人妻中文字幕| 国产午精品午夜福利757视频播放| 少妇精品无码一区二区免费视频| 日本一区二区国产在线| 亚洲日本一区二区三区在线播放 | 亚洲国产精品乱码一区二区| 亚洲欧美日韩在线码| 日韩不卡一区二区三区四区| japanese人妻中文字幕| 久久一日本道色综合久久| 亚洲男人精品青春的天堂| 色婷婷五月综合亚洲小说| 国产午夜福利视频一区二区| 99久久精品费精品国产一区二| 国产日韩综合av在线| 免费a级毛片18以上观看精品| 亚洲av一区二区在线看| 精品少妇爆乳无码aⅴ区| 欧美日韩中文字幕视频不卡一二区| 酉阳| 成人免费av色资源日日| 欧美国产精品啪啪| 日韩精品人妻中文字幕| 不卡一区二区三区视频播放| 亚洲国产成人久久77| 国产欧美另类久久久精品不卡| 亚洲AV无码不卡在线播放| 国产亚洲精品综合99久久| 久久久久青草线综合超碰| 久热这里只有精品12| 国产美女免费永久无遮挡| 亚洲男人的天堂久久香蕉| 蜜臀一区二区三区精品免费| 在线观看成人永久免费网站| 国产成人a∨激情视频厨房|