網站首頁 編程語言 正文
從大學就開始做C#這塊,也做C#幾年了,最近又從ios轉回.Net,繼續做C#,之前也沒有寫博客的習慣,寫博客也是從我做ios的時候開始的,現在既然又做回了.net,那就寫點關于.Net的博客,可能在大牛眼里這些都是簡單基礎的,不過回過頭看我當時初學的時候覺得委托事件是不容易理解的,我這里也是想著聯系著OC,兩者有比較的學習下。畢竟都是面向對象語言,思想是相通的。
委托在OC中類似block,都是指向一個函數,其實他沒和C++的函數指針類似。但委托還是和函數指針不太一樣,委托是完全面向對象的,是類型安全可靠的。C++的指針僅僅指向成員函數,而委托同時封裝了一個對象實例和方法。
委托聲明用于定義一個從System.Delegate類派生的類。
格式:屬性集 修飾符 delegate 返回值類型(A) 標識符(C)(形參列表(B));
一、委托是什么?
看上面的紅字我們可以明白其實委托是一個類。其實類是什么?類也是一種數據類型,它了String類一樣,也是一個數據類型,所以呢委托其實也是一個數據類型,只是這個數據類型和其他的有點不同,它這個數據類型指向的是一個函數。一個返回值為A,形參列表為B的名為標識符C的函數。其實這和OC中的block類似,block中也是用來定義函數。我們用typedef void(^myblock1)(int a,int b);來定義一個block,其實就是定義一個數據類型。上面的委托聲明也是定義了一個引用類型的數據類型。
二、委托怎么用?
上面也說了,聲明一個委托其實就是聲明了一個數據類型,和Person、String一樣都是一個數據類型。我們在使用委托和使用Person、String類型的數據一樣。也是先聲明:public 類型(Person、String) 變量(或屬性)名。所以我們在使用委托時也是這樣。只是這個變量或屬性對應的是一個函數。
三、例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
//定義了一個從System.Delegate類派生的類
//也可以理解為一種數據類型 這種數據類型指向返回值為void 參數為Person對象的函數
//我們也可以把Person類理解為一種數據類型 只是它包含的是Name和Age
public delegate void EatFood(Person p);
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
//既然委托是一數據類型和String一樣,所以可以像聲明String對象一樣聲明代理變量
public EatFood eatFood;
public void eating()
{
if (eatFood != null)
{
eatFood(this);
}
}
}
}
上面定義了一個Person類,也定義了一個定義了一個從System.Delegate類派生的類EatFood,同時在Person類中聲明了EatFood類類型的一個變量,在eating()函數中使用了這個變量。ps:請留意上面代碼中的注釋。下面的代碼中我們定義了兩個Person對象,一個chinesePerson一個englishPerson,而分別為兩個類的eatFood變量指定不同的函數。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
class Program
{
static void Main(string[] args)
{
Person chinesePerson = new Person("小明",25);
//通過構造函數實例化對象
chinesePerson.eatFood = new EatFood(chineseEat);
chinesePerson.eating();
Console.WriteLine("--------------------------------------");
Person englishPerson = new Person("Ivan",25);
//通過直接復制來實例化對象
englishPerson.eatFood = englishEat;
englishPerson.eating();
Console.ReadLine();
}
static void chineseEat(Person p)
{
Console.WriteLine("我是{0},我今年{1}歲了,我吃饅頭",p.Name,p.Age);
}
static void englishEat(Person p)
{
Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age);
}
}
}
可以看到針對不同的對象指定不同的eatFood變量則執行的結果也不一樣。
四、委托和其他數據類型的區別
上面也說了可以把委托當做是一個數據類型,但它和普通的數據類型還是有區別的。這可能就是現在的個性吧,委托也有委托的個性。
委托實例化用于創建委托實例,和類實例創建語法相同。但委托可以封裝多個方法,這些方法的集合合稱為調用列表。委托使用+、+=、-、-=運算符向調用列表中增加或刪除方法。
我們對上面的代碼稍作改動,Person類不用改,只改Main方法中的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
class Program
{
static void Main(string[] args)
{
Person chinesePerson = new Person("小明",25);
//通過構造函數實例化對象
chinesePerson.eatFood = new EatFood(chineseEat);
chinesePerson.eatFood += englishEat;
chinesePerson.eating();
Console.WriteLine("--------------------------------------");
Person englishPerson = new Person("Ivan",25);
//通過直接復制來實例化對象
englishPerson.eatFood = englishEat;
englishPerson.eatFood += chineseEat;
englishPerson.eating();
Console.ReadLine();
}
static void chineseEat(Person p)
{
Console.WriteLine("我是{0},我今年{1}歲了,我吃饅頭",p.Name,p.Age);
}
static void englishEat(Person p)
{
Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age);
}
}
}
為了促進中西交流,好多人去留學也有好多來到中國的,所以在吃的方面彼此都會吃對方的。所以要增加方法來表示吃不同的食物。有了委托可以通過+=、-=來實現增加、刪除調用列表,這樣就方面很多。從下面的輸出結果能看到,每個Person對象都調用了chineseEat、englishEat函數。
五、好處
上面的demo也展示了委托的使用方法,通過上面的使用我們可以思考下使用它的好處。我們如果不使用委托來實現這個功能的話,我們可能會在Person類中做一個判斷,判斷下是Chinses還是English,可是這樣的話,如果哪天有了日本、法國等,那又要多好多個判斷。可擴展性不好。可能有的會說可以在Person里面定義一個虛方法,分別聲明Chinese、English類繼承Person類重寫虛方法,這確實是一個方法,如果有新的要擴展的話可以直接創建一個新的類重寫虛方法就搞定了,不過這樣的話如果只是這個方法不同,就要寫一個類,這樣未免殺雞用牛刀了。所以說委托還是一個不錯的選擇。如果不僅要增加語言還要增加方法那這就更麻煩了。有了委托這些全解決。
六、事件
對象之間的交互是通過消息傳遞來實現的,而事件就是對象發送的消息,通過發信號的形式通知操作的發生。引發事件的對象為事件發送方,捕獲事件并對其做出響應的對象為事件接收方。在事件通信中,事件發送方不知哪個對象或方法將接收它引發的事件,所需要的是在發送方和接收方之間用一個紐帶來聯系,在C#中使用委托為這個紐帶。
事件聲明的格式:屬性集 修飾符 event 委托類型 事件名。
其實說白了就是事件是對委托變量的封裝。請注意上面寫的,我一直寫的是委托類型的變量,面向對象的三大特征之一就是封裝,例如變量和屬性。在上面直接使用委托來指定函數,其實這和直接使用變量一樣,但是在面向對象中一般不會直接訪問變量,而是對變量進行封裝,例如屬性{get;set;}方法。事件是對委托的封裝。我們來看一下事件的使用,和上面使用委托一樣,我們在Person類中聲明一個事件。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
//定義了一個從System.Delegate類派生的類
//也可以理解為一種數據類型 這種數據類型指向返回值為void 參數為Person對象的函數
//我們也可以把Person類理解為一種數據類型 只是它包含的是Name和Age
public delegate void EatFoodDelegate(Person p);
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
//既然委托是一數據類型和String一樣,所以可以像聲明String對象一樣聲明代理變量
//public EatFood eatFood;
//之前是直接聲明委托,現在是聲明一個事件
public event EatFoodDelegate EatFoodEventHandler;
public void eating()
{
if (EatFoodEventHandler != null)
{
EatFoodEventHandler(this);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
class Program
{
static void Main(string[] args)
{
Person chinesePerson = new Person("小明",25);
//通過構造函數實例化對象
chinesePerson.EatFoodEventHandler += new EatFoodDelegate(chineseEat);
chinesePerson.EatFoodEventHandler += englishEat;
chinesePerson.eating();
Console.WriteLine("--------------------------------------");
Person englishPerson = new Person("Ivan", 25);
//在委托中 可以直接使用=來給委托對象復制 而在事件中就不能直接使用= 要使用+=
englishPerson.EatFoodEventHandler += new EatFoodDelegate(englishEat);
englishPerson.EatFoodEventHandler += chineseEat;
englishPerson.eating();
Console.ReadLine();
}
static void chineseEat(Person p)
{
Console.WriteLine("我是{0},我今年{1}歲了,我吃饅頭",p.Name,p.Age);
}
static void englishEat(Person p)
{
Console.WriteLine("I'm {0},I am {1} , I eat MianBao",p.Name,p.Age);
}
}
}
上面可以看到,使用事件來實現了同樣的功能。
七、委托和代理設計模式的區別
不管是使用委托或者事件其實它們都是在A對象(本例中的Person對象)中調用B對象中的方法,這與設計模式中有相似之處。具體代理設計模式這里就省略了,委托和代理都是在A對象使用B對象中的方法。不過它們還是有區別的,委托中在A中直接使用的是B中的方法,是類與方法之間的,代理設計模式中是將A類中設置一個B類變量,然后通過B來使用B中的方法,是類與類之間的。這也是我的個人理解,不知道對不對,錯了的話也希望大牛指正,以免耽誤了其他的社會主義接班人。
原文鏈接:https://www.cnblogs.com/5ishare/p/5745146.html
相關推薦
- 2023-04-03 Input系統分發策略及其應用示例詳解_Android
- 2022-07-01 詳解如何在Go語言中調用C源代碼_Golang
- 2022-09-08 go語言中函數與方法介紹_Golang
- 2022-08-23 Python可視化模塊altair的使用詳解_python
- 2022-06-06 ceph集群RadosGW對象存儲使用詳解_其它綜合
- 2022-12-04 Flask帶參URL傳值的實現方法_python
- 2022-08-10 Go語言pointer及switch?fallthrough實戰詳解_Golang
- 2024-04-08 SpringBoot使用Mybatis-Plus中分頁出現total=0的情況解決
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支