C#中委托的基本概念介紹
最近在看深入理解C#,發(fā)現(xiàn)這是一本很不錯(cuò)的書,將很多C#的知識點(diǎn)聯(lián)系了起來,更像是一本C#歷史書,從C# 1一步步介紹到C# 4。
所以準(zhǔn)備一邊看,一邊整理讀書筆記。那么就先從委托開始。
委托是C#中一個(gè)非常重要的概念,從C# 1開始就有了委托這個(gè)核心概念,在C# 2和C# 3中委托又有了很多改進(jìn)。
通過委托,我們可以將一個(gè)方法當(dāng)作對象封裝起來,并且在運(yùn)行時(shí),我們可以通過這個(gè)對象來完成方法的調(diào)用。
委托的使用
首先,來個(gè)簡單的例子,蘋果只負(fù)責(zé)設(shè)計(jì)iphone,而把組裝iphone的工作委托給富士康做。
class Apple
{
//聲明委托類型
public delegate void AssembleIphoneHandler();
public AssembleIphoneHandler AssembleIphone;
public void DesignIphone()
{
Console.WriteLine("Design Iphone By Apple");
}
}
class Foxconn
{
//與委托類型簽名相同的方法
public void AssembleIphone()
{
Console.WriteLine("Assemble Iphone By Foxconn");
}
}
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple();
Foxconn foxconn = new Foxconn();
//創(chuàng)建委托實(shí)例
apple.AssembleIphone = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);
apple.DesignIphone();
//委托實(shí)例的調(diào)用
apple.AssembleIphone();
//通過Invoke進(jìn)行顯示調(diào)用
//apple.AssembleIphone.Invoke();
Console.Read();
}
}
從上面的例子中,可以體會(huì)一下委托的使用。使用委托需要滿足4個(gè)條件:
1.聲明一個(gè)委托類型
2.找到一個(gè)跟委托類型具有相同簽名的方法(可以是實(shí)例方法,也可以是靜態(tài)方法)
3.通過相同簽名的方法來創(chuàng)建一個(gè)委托實(shí)例
4.通過委托實(shí)例的調(diào)用完成對方法的調(diào)用
委托類型和委托實(shí)例
當(dāng)我們使用委托的時(shí)候,一定要注意這兩個(gè)概念。
委托類型,是通過delegate關(guān)鍵字聲明的一種類型,例如上面例子中的:
public delegate void AssembleIphoneHandler();
注意,"AssembleIphoneHandler"是一個(gè)委托類型,它有自己的方法,可以創(chuàng)建相關(guān)的實(shí)例。通過"ILSpy"可以看到"AssembleIphoneHandler"的方法以及父類信息。
委托類型的聲明過程中描述了該委托類型的簽名(返回類型,參數(shù)列表),這個(gè)簽名就決定了那個(gè)方法可以用來創(chuàng)建一個(gè)改委托類型的委托實(shí)例;同時(shí),這個(gè)簽名還表示了該委托實(shí)例調(diào)用的簽名。
而委托實(shí)例,就是通過委托類型進(jìn)行實(shí)例化的對象,例如上面例子中的:
apple.AssembleIphone = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);
在創(chuàng)建委托實(shí)例的過程中,我們需要找到一個(gè)跟委托類型簽名相同的方法來完成委托實(shí)例的創(chuàng)建。
委托的合并和刪除
在前面的例子中,委托實(shí)例(apple.AssembleIphone)只對應(yīng)一個(gè)操作(方法foxconn.AssembleIphone)。但是,每個(gè)委托實(shí)例都有一個(gè)操作列表,稱為委托實(shí)例的調(diào)用列表(invocation list)。在System.Delegate類中,有兩個(gè)靜態(tài)方法Combine和Remove,通過這兩個(gè)靜態(tài)方法,我們就可以進(jìn)行兩個(gè)委托實(shí)例的調(diào)用列表的合并和刪除。
接著上面的例子進(jìn)行修改,這次來看看委托的合并和刪除。
class Foxconn
{
//與委托類型簽名相同的方法
public void AssembleIphone()
{
Console.WriteLine("Assemble Iphone By Foxconn");
}
public void PackIphone()
{
Console.WriteLine("Pack Ipnone By Foxconn");
}
public void ShipIphone()
{
Console.WriteLine("Ship Iphone By Foxconn");
}
}
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple();
Foxconn foxconn = new Foxconn();
//創(chuàng)建委托實(shí)例
apple.AssembleIphone = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);
//apple.AssembleIphone += new Apple.AssembleIphoneHandler(foxconn.PackIphone);
apple.AssembleIphone = (Apple.AssembleIphoneHandler)Delegate.Combine(apple.AssembleIphone, new Apple.AssembleIphoneHandler(foxconn.PackIphone));
apple.AssembleIphone += new Apple.AssembleIphoneHandler(foxconn.ShipIphone);
apple.DesignIphone();
//委托實(shí)例的調(diào)用
apple.AssembleIphone();
//通過Invoke進(jìn)行顯示調(diào)用
//apple.AssembleIphone.Invoke();
Console.Read();
}
}
這次,我們在Foxconn類中加了打包和運(yùn)輸?shù)姆椒?,這樣,可以通過Combine方法將組裝、打包和運(yùn)輸三個(gè)操作合并到委托實(shí)例apple.AssembleIphone的調(diào)用列表中。
當(dāng)我們調(diào)用委托實(shí)例的時(shí)候,委托實(shí)例的調(diào)用列表中的所有操作會(huì)依次被執(zhí)行。
注意,一般在代碼中,很少直接使用Combine和Remove方法的顯式調(diào)用,而是通過"+="和"-="操作符來實(shí)現(xiàn)。
委托是不易變的
這里有一點(diǎn)要提的是,委托是不易變的,一旦創(chuàng)建了一個(gè)委托實(shí)例后,這個(gè)實(shí)例的所有內(nèi)容都不能被改變了(就像string一樣,string也是不易變的)。
所以說Combine和Remove都沒有改變委托實(shí)例,都是新建了一個(gè)委托實(shí)例。
委托調(diào)用列表
在使用調(diào)用列表的時(shí)候,有些關(guān)鍵點(diǎn)需要注意一下,假如說一個(gè)委托實(shí)例的調(diào)用列表為[methodA, methodB, methodC]。那么當(dāng)我們調(diào)用委托實(shí)例的時(shí)候,methodA, methodB, methodC會(huì)依次被執(zhí)行。
1.如果上面A-C三個(gè)方法都有返回值,我們只能得到最后一個(gè)操作的返回值,其他的返回值都將被忽略。
2.如果調(diào)用列表中的一個(gè)操作有異常,那么所有的下游操作都不會(huì)被執(zhí)行。
舉例,我們在PackIphone方法中加入一個(gè)異常,那么委托列表中的ShipIphone操作將不會(huì)被執(zhí)行到:
class Foxconn
{
//與委托類型簽名相同的方法
public void AssembleIphone()
{
Console.WriteLine("Assemble Iphone By Foxconn");
}
public void PackIphone()
{
throw new NotImplementedException();
}
public void ShipIphone()
{
Console.WriteLine("Ship Iphone By Foxconn");
}
}
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple();
Foxconn foxconn = new Foxconn();
//創(chuàng)建委托實(shí)例
apple.AssembleIphone = new Apple.AssembleIphoneHandler(foxconn.AssembleIphone);
//apple.AssembleIphone += new Apple.AssembleIphoneHandler(foxconn.PackIphone);
apple.AssembleIphone = (Apple.AssembleIphoneHandler)Delegate.Combine(apple.AssembleIphone, new Apple.AssembleIphoneHandler(foxconn.PackIphone));
apple.AssembleIphone += new Apple.AssembleIphoneHandler(foxconn.ShipIphone);
apple.DesignIphone();
//委托實(shí)例的調(diào)用
try
{
apple.AssembleIphone();
}
catch
{
Console.WriteLine("an exception happened");
}
Console.Read();
}
}
GetInvocationList
對于上面兩個(gè)問題,我們可以通過委托實(shí)例的GetInvocationList()方法,通過這個(gè)方法可以得到調(diào)用列表中的所有操作。
這樣,就可以顯示調(diào)用委托來進(jìn)行異常處理或者返回值的保存。
foreach (Apple.AssembleIphoneHandler method in apple.AssembleIphone.GetInvocationList())
{
try
{
method();
}
catch
{
Console.WriteLine("an exception happened");
}
}
總結(jié)
本文介紹了委托的基本概念,以及委托類型和委托實(shí)例的區(qū)別。
委托本質(zhì)上是一個(gè)派生自System.MulticastDelegate的類,我們可以通過特定的(與委托類型簽名相同)的方法創(chuàng)建委托實(shí)例,通過委托是,可以間接完成某些操作。
同時(shí),可以通過Combine和Remove操作來進(jìn)行委托實(shí)例的調(diào)用列表的合并和刪除。
相關(guān)文章
WPF實(shí)現(xiàn)監(jiān)聽快捷鍵的方式分享
這篇文章主要為大家詳細(xì)介紹了WPF實(shí)現(xiàn)監(jiān)聽快捷鍵的幾種方式,文中的示例代碼講解詳細(xì),具有一定的借鑒與學(xué)習(xí)價(jià)值,需要的可以了解一下2023-03-03C#實(shí)現(xiàn)讀取匿名對象屬性值的方法示例總結(jié)
這篇文章主要介紹了C#實(shí)現(xiàn)讀取匿名對象屬性值的方法,結(jié)合實(shí)例形式總結(jié)分析了C#通過反射、轉(zhuǎn)換等方法讀取匿名對象屬性值的相關(guān)操作技巧,需要的朋友可以參考下2020-03-03c# 如何實(shí)現(xiàn)一個(gè)簡單的json解析器
這篇文章主要介紹了c# 如何實(shí)現(xiàn)一個(gè)簡單的json解析器,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07C#實(shí)現(xiàn)將Doc文檔轉(zhuǎn)換成rtf格式的方法示例
這篇文章主要介紹了C#實(shí)現(xiàn)將Doc文檔轉(zhuǎn)換成rtf格式的方法,結(jié)合實(shí)例形式分析了C#針對word文件的讀取及文檔格式轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2017-07-07