1. 程式人生 > >理解什麼是委託、事件、Lambad表示式,從回撥說起!

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

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

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

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,這塊知識終於理順了,部落格也整理的幾個小時,希望對大家有用!

         
 

相關推薦

理解什麼是委託事件Lambad表示式

       接觸委託、事件等知識好長時間了,也反反覆覆看了很多資料,都是一來就給我講委託的語法、用法,卻沒有告訴我到底什麼是委託。要知道什麼是委託,先從回撥說起!        提示:該博文適合已經學習過委託、事件、Lambad表示式,但學的很模糊的朋友學習,如果你從未學

初步理解委託事件匿名方法和Lambda

最經在學習LinqtoSql,然後扯到Lambda表示式,然後扯到匿名方法,然後扯到委託,最後扯到事件處理。。。後來發現對委託這個概念和事件處理這個過程理解得不是很清晰,遂得一下學習筆記。那裡說得不對,請大家多多指教! 第一部分:理解委託 委託委託,顧名思義,就是類似

【轉】【UNITY3D 遊戲開發之七】C# 中的委託事件匿名函式Lambda 表示式

"委託是一個類,它定義了方法的型別,使得可以將方法當作另一個方法的引數來進行傳遞,這種將方法動態地賦給引數的做法,可以避免在程式中大量使用If-Else(Switch)語句,同時使得程式具有更好的可擴充套件性。" from: http://www.himigame.com/

.NET基礎之委託事件反射與特性

轉自:http://www.cnblogs.com/edisonchou/p/4827578.html   委託、事件、反射與特性  Index :  (1)型別語法、記憶體管理和垃圾回收基礎  (2)面向物件的實現和異常的處理

委託事件單例在Unity3D中的使用

Here I demonstrate how to create delegates, events and singletons to work in conjunction. This tutorial is written for Unity3D,

.NET基礎拾遺(4)委託事件反射與特性

一、委託基礎 1.1 簡述委託的基本原理   委託這個概念對C++程式設計師來說並不陌生,因為它和C++中的函式指標非常類似,很多碼農也喜歡稱委託為安全的函式指標。無論這一說法是否正確,委託的的確確實現了和函式指標類似的功能,那就是提供了程式回撥指定方法的機制。   在委託內部,包含了一個指向某個方

DOM基礎知識(概念節點樹事件Document)

接口 鼠標事件 所有 orm selector 設置 部分 子節點 var 1、 DOM概念 全稱為 Document Object Model,譯為文檔對象模型 D:文檔 - DOM將HTML頁面解析為一個文檔 —> document對象 O:對象 - DOM將

Vue:渲染指令事件組件PropsSlots

stop cnblogs not gui child 異步 意義 們的 最好 如果要我用一句話描述使用 Vue 的經歷,我可能會說“它如此合乎常理”或者“它提供給我需要的工具,而且沒有妨礙我的工作”。每當學習 Vue 的時

0514JS操作document對象事件(this)

innerhtml title AI div HA 得到 ack 多個 -a |js操作document對象|-找到對象|--document.getElementById("id名"); 通過id名找到唯一的對象 var duixiang = document.getE

監聽器事件事件事件註冊深度剖析

left local ets clas imp for 圖形 沒有 執行 AWT事件處理 事件處理機制,幾類具有典型代表意義的事件: 幾種常用事件處理機制 查看JDK Doc文檔中的 用戶圖形界面的行為 1、事件類型 (1)事件類的層次

前端(十二)—— JavaScript基礎操作:if語句for循環while循環for...infor...of異常處理函數事件JS選擇器JS操作頁面樣式

結束 建議 prop map、set -c 表單元素 tle form collect JavaScript基礎操作 一、分支結構 1、if語句 if 基礎語法 if (條件表達式) { 代碼塊; } // 當條件表達式結果為true,會執行代碼塊;反之不執行

day031同步鎖訊號量事件佇列生成者消費者模型Jionablequeue

multiprocessing模組主要內容: 1、守護程序 2、程序同步 1.同步鎖(*****) 2.訊號量 3.事件 3、程序通訊 1.佇列(*****) 2.生產者消費者模型,JoinableQueue, 主要在佇列的基礎上多了兩個功能:q.task_done,

Python筆記day56(jQuery)|文件處理事件動畫效果eachdata

1,文件處理 新增到指定元素內部的後面 $(A).append(B)// 把B追加到A $(A).appendTo(B)// 把A追加到B 新增到指定元素內部的前面 $(A).prepend(B)// 把B前置到A $(A).prependTo(

iOS原生OC向React Native傳送訊息事件通知

RCTEventEmitter 此篇僅獻給剛剛入門的同志們。 大家在使用React Native的時候,都會比較關心原生和React Native的互動問題。React Native給原生髮送訊息,在中文官網上講得也比較明白,按照上面的例子,相信大家都可以實現出來。但是在原生給React Native傳送

mysql小白之旅——進階篇2——儲存過程事件觸發器事務分散式事務

14.儲存過程的寫法 1)定義條件和處理 --CONDITION --HANDLER 2)游標 CURSOR --DECLARE --OPEN --FETCH --CLOSE 3)流程控制 --IF語句 --CASE語句 --LOOP語句 --LEAVE語句  相當於br

毛毛Python進階之路4——訊號量事件佇列生產者消費者模型管道程序池及其返回值

毛毛Python進階之路4——訊號量、事件、佇列、生產者消費者模型、管道、程序池及其返回值! 1、訊號量 上次我們講到了鎖,可以控制某段程式在同一時間內只能被一個程序鎖訪問。現在我想被兩個程序訪問,或者更多怎麼辦了?訊號量就由此而生! 這就是訊號量做的事!這段程式我可以指定

高亮:單關鍵詞多關鍵詞多組多關鍵詞簡單到複雜實現滿足多方面需求的頁面關鍵詞高亮

前言 我的前言都是良心話,還是姑且看一下吧: 別人一看這個標題,心想,“怎麼又是一個老到掉牙的需求,網上一搜一大堆解決方案啦!”。沒錯!這個需求實在老土得不能再老土了,我真不想寫這樣一種需求的文章,無奈!無奈! 現實的情況是我想這麼舊的需求網上資料一大把一大把,雖然我知道網上資料可能有坑,但是我總不信找

Java程式設計之委託代理內部類以及匿名內部類(閉包)

最近一直在看Java的相關東西,因為我們在iOS開發是,無論是Objective-C還是Swift中,經常會用到委託代理回撥,以及Block回撥或者說是閉包回撥。接下來我們就來看看Java語言中是如何實現委託代理回撥以及閉包回撥的。當然這兩個技術點雖然實現起來並不困難,但是,這回調在封裝一些公用元件時還是特別

Python之路(第三十八篇) 併發程式設計:程序同步鎖/互斥鎖訊號量事件佇列生產者消費者模型

一、程序鎖(同步鎖/互斥鎖) 程序之間資料不共享,但是共享同一套檔案系統,所以訪問同一個檔案,或同一個列印終端,是沒有問題的, 而共享帶來的是競爭,競爭帶來的結果就是錯亂,如何控制,就是加鎖處理。 例子 #併發執行,效率高,但競爭同一列印終端,帶來了列印錯亂 from multiproc

jQuery的屬性css文件處理事件選擇器以及jsp要點簡記

本文繼續紀錄學習jQuery,主要學習jQuery的屬性、css、文件處理、事件、選擇器,最後簡要記錄jsp知識要點。 1.JQ屬性: attr(name|pro|key,val|fn):設定或返回被