理解什麼是委託、事件、Lambad表達式,從回調說起!

       接觸委託、事件等知識好長時間了,也反反覆覆看了很多資料,都是一來就給我講委託的語法、用法,卻沒有告訴我到底什麼是委託。要知道什麼是委託,先從回調說起!

       提示:該博文適合已經學習過委託、事件、Lambad表達式,但學的很模糊的朋友學習,如果你從未學習過這些東西,請看完第一節“1.什麼是回調?”之後,通過其他更基礎的資料學習之後再繼續往下學習。在學習過程中,希望搭建一個項目跟着做一遍,這樣更好理解。如文中有錯亂之處,望指正!

       Demo下載地址:http://download.csdn.net/detail/wb09100310/8495759

1.什麼是回調?

       所謂回調,是指我在A類調用B類裏的一個方法,在B類的這個方法執行到某個時刻或條件的時候,又返回來調用A類的方法。這是我對回調的理解,先來看一個列子就好理解了。

示例:

       創建一個控制檯應用程序,命名爲DelegateTest,然後添加一個Product類,結構如下圖:

      

 

      我們先看一下Profram.cs和Product的代碼,再來解釋。

      Program.cs代碼:

     

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelegateTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product(){Name = "iPhone6",Price = 5288};//對象初始化語法
            product.GetInfo(product);
            Console.ReadKey();
        }

        public static void Output(Product product)
        {
            Console.Write("產品名稱:");
            Console.WriteLine(product.Name);
            Console.Write("產品價格:");
            Console.WriteLine(product.Price);
        }
    }
}


      Product.cs代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DelegateTest
{
    /// <summary>
    /// 產品類
    /// </summary>
    public class Product
    {
        /// <summary>
        /// 產品名字
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 產品價格
        /// </summary>
        public int Price { get; set; }

        public void GetInfo(Product product)
        {
            if (product != null)
            {
                Program.Output(product);
            }

        }
    }
}


       解釋一下,是這樣的。在Program的Main方法裏,我們通過初始化語法實例化了一個Product類,然後調用Product類裏的GetInfo方法得到產品信息,而GetInfo方法又反過來調用Program類裏的Output方法輸出產品信息,這就是回調。運行結果:

       

        這裏我們沒有用委託,就用我們很普通的方法實現了回調。然而,我們在實際開發過程中是Product類應該被放到實體層的,而GetInfo方法應該是在業務邏輯層的,也就是說,他們直接的命名空間是不同的。爲了說明,這裏我就把Product類放到Model層,我們創建一個類庫,命名爲Model,然後在Model裏添加一個類Product,並把之前Product的類拷貝過來,如果你是直接從DelegateTest裏直接把Product類文件複製過來,那請修改Product類的命名空間爲Model,結構如圖:

       

       這時候,我們發現Program的Main方法裏找不到Product類了,添加對Model層的引用後可以了。但是GetInfo方法裏找不到Program類了,那我們能不能在Model層反過來添加對DelegateTest的對應呢?不可以的,這時候就可以用委託實現上述回調功能。

      

 

2.委託

       怎麼定義委託,怎麼使用委託就不說了,網上有的是資料。這裏是如何理解委託,直接看代碼,然後再解釋。

       Product類中添加委託:

        /// <summary>
        /// 定義一個委託
        /// </summary>
        /// <param name="product"></param>
        public delegate void OutputHandler(Product product);
        /// <summary>
        /// 定義委託成員變量
        /// </summary>
        private OutputHandler outputHandler;
        /// <summary>
        /// 註冊委託方法
        /// </summary>
        /// <param name="handler"></param>
        public void RegHandler(OutputHandler handler)
        {
            outputHandler = handler;
        }

      並修改GetInfo方法爲:

        /// <summary>
        /// 得到產品信息
        /// </summary>
        /// <param name="product"></param>
        public void GetInfo(Product product)
        {
            if (product != null && outputHandler!=null)
            {
                outputHandler(product);
            }

        }



 

       Program裏的Main方法修改如下:

        static void Main(string[] args)
        {
            Product product = new Product(){Name = "iPhone6",Price = 5288};//對象初始化語法
            product.RegHandler(Output);//將Output方法註冊到委託中
            product.GetInfo(product);
            Console.ReadKey();
        }

 

       運行結果同上,解釋一下:

       Product:定義了一個委託,並定義了一個委託變量,由於我們的委託變量爲私有的,所以我們還需要再定義一個註冊方法到委託變量的方法RegHandler。在GetInfo中,我們修改後爲通過委託變量來回調Program中註冊到委託變量中的Output方法,這樣就輸出了產品信息。

       Program:紅色部分,將Output方法註冊到委託變量,這樣在調用GetInfo方法的時候自然就能回調到Output方法了。

       到這裏應該能夠理解委託中回調是怎麼回事了,也就基本上理解什麼委託了,那麼什麼又是事件呢?

 

3.事件

       事件實際上就是簡化了委託的寫法,主要是簡化了註冊方法。還是一樣,先看代碼,然後再來解釋。

       Product類中委託修改爲:

        /// <summary>
        /// 定義一個委託
        /// </summary>
        /// <param name="product"></param>
        public delegate void OutputHandler(Product product);
        /// <summary>
        /// 定義事件
        /// </summary>
        public event OutputHandler outputHandler;
    刪除了RegHandler方法
     Program裏的Main方法修改如下:
       static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//對象初始化語法
            product.outputHandler += Output;//註冊事件處理程序
            product.GetInfo(product);
            Console.ReadKey();
        }


 


         運行結果同上,加了event關鍵字之後稱之爲事件,在註冊方法的時候用“+=”符號就行,可以添加多個方法。移除方法列表上的方法用“-+”符號。接下來我們再修改一下,以便讓它看上去符合微軟推薦的事件模式。

         先看一下EventArgs類

        

         

    // 摘要: 
    //System.EventArgs 是包含事件數據的類的基類。
    [Serializable]
    [ComVisible(true)]
    public class EventArgs
    {
        // 摘要: 
        // 表示沒有事件數據的事件。
        public static readonly EventArgs Empty;

        // 摘要: 
        // 初始化 System.EventArgs 類的新實例。
        public EventArgs();
    }

         EventArgs表示一個不發任何自定義消息的事件。對於簡單的事件來說,傳遞的參數是EventArgs實力,如果要自定義傳遞數據參數,需要重新定義一個類,並繼承EventArgs,我們的事件要傳遞的Product類,所以定義應該如下:

        

    public class ProductEventArgs:EventArgs
    {
        public readonly Product product;

        public ProductEventArgs(Product p)
        {
            product = p;
        }
    }


        然後我們的委託修改一下,其他不變。

      

        /// <summary>
        /// 定義一個委託
        /// </summary>
        /// <param name="product"></param>
        public delegate void OutputHandler(object sender,ProductEventArgs e);


         是不是很像ASP.NET控件的事件方法,sender是觸發事件的對象,e是要傳遞的事件參數。GetInfo方法修改如下,
       

        /// <summary>
        /// 得到產品信息
        /// </summary>
        /// <param name="product"></param>
        public void GetInfo(Product product)
        {
            if (product != null && outputHandler!=null)
            {
                outputHandler(this,new ProductEventArgs(product));
            }

        }


          如此,Program類的Output方法修改如下,其他不變:

    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//對象初始化語法
            product.outputHandler += Output;//註冊事件處理程序
            product.GetInfo(product);
            Console.ReadKey();
        }

        public static void Output(object sender,ProductEventArgs e)
        {
            Console.Write("產品名稱:");
            Console.WriteLine(e.product.Name);
            Console.Write("產品價格:");
            Console.WriteLine(e.product.Price);
        }
    }

         上面只是依照微軟的事件模式,效果和前面是一樣的。

 

4.匿名方法

        思考一下我們發現,Output方法很少會被委託之外的任何程序調用,手工定義一個由委託對象調用的獨立方法會顯的很繁瑣,於是C#提供了匿名方法,我們可以如下改下Program類,Output方法需要了,運行結果同上:


 

    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//對象初始化語法
            //product.outputHandler += Output;//註冊事件處理程序
            product.outputHandler += delegate(object sender, ProductEventArgs e)
            {
                Console.Write("產品名稱:");
                Console.WriteLine(e.product.Name);
                Console.Write("產品價格:");
                Console.WriteLine(e.product.Price);
            };
            product.GetInfo(product);
            Console.ReadKey();
        }

    }


5.Lambad表達式

         Lambad表達式只是用更簡單的方法來寫匿名方法,先貼代碼再說,爲了容易理解,我們再在Product類裏添加一個委託

        

        /// <summary>
        /// 定義一個委託
        /// </summary>
        /// <param name="product"></param>
        public delegate int CompareHandler(int p);

          如果價格大於5000返回1,否則返回0,Program類修改如下:

         

    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//對象初始化語法
            product.GetInfo(product);
            Product.CompareHandler c = i => i > 5000 ? 1 : 0;
            int r = c(product.Price);
            Console.Write(r);
            Console.ReadKey();
        }

    }


        解釋一下:i是參數列表,和定義一個方法參數列表一樣,多個參數用“,”隔開,可以加上參數類型,=>符號後面的語句是處理參數列表的語句。那如果是輸出之前的產品信息該怎麼寫呢?,Progarm修改如下:

       

class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//對象初始化語法
            //product.outputHandler += Output;//註冊事件處理程序
            //product.outputHandler += delegate(object sender, ProductEventArgs e)
            //{
            //    Console.Write("產品名稱:");
            //    Console.WriteLine(e.product.Name);
            //    Console.Write("產品價格:");
            //    Console.WriteLine(e.product.Price);
            //};
            //product.GetInfo(product);
            product.outputHandler += (object sender, ProductEventArgs e) =>
            {
                Console.Write("產品名稱:");
                Console.WriteLine(e.product.Name);
                Console.Write("產品價格:");
                Console.WriteLine(e.product.Price);
            };
            product.GetInfo(product);
            Console.ReadKey();
        }

    }

    

        OK,這塊知識終於理順了,博客也整理的幾個小時,希望對大家有用!

         
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章