C#沉淀之委托的深入講解
什么是委托
要傳遞方法,就必須把方法的細(xì)節(jié)封裝在一鐘新類型的對象中,即委托。委托是一種特殊類型的對象,其特殊之處在于,我們以前定義的所有對象都包含數(shù)據(jù),而委托只包含一個或多個方法的地址。
.NET版本中,委托指向方法的地址。在C++中,函數(shù)指針是一個指向內(nèi)存位置的指針,但它不是類型安全的。開發(fā)者無法判斷這個指針實際指向什么,像參數(shù)和返回值等項就更不知道了。
.NET委托是類型安全的類,它定義了返回類型和參數(shù)的類型。委托類不僅包含對方法的引用,也可以包含對多個方法的引用。
可以認(rèn)為委托是持有一個或多個方法的對象。委托可以被執(zhí)行,執(zhí)行委托時委托會執(zhí)行它所“持有”的方法
代碼示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
//使用關(guān)鍵字delegate聲明委托類型
//委托是一種類型,所以它與類屬于同一級別
//注意:這里是委托類型,而不是委托對象
delegate void MyDel(int value);
class Program
{
void PrintLow(int value)
{
Console.WriteLine("{0} - Low Value", value);
}
void PrintHigh(int value)
{
Console.WriteLine("{0} - High Value", value);
}
static void Main(string[] args)
{
//實例化Program類,以訪問PrintLow和PrintHigh方法
Program program = new Program();
//聲明一個MyDel類型的委托
MyDel del;
//創(chuàng)建隨機數(shù)
Random rand = new Random();
int randomvalue = rand.Next(99);
//使用三目運算符根據(jù)當(dāng)前隨機數(shù)的值來創(chuàng)建委托對象
del = randomvalue < 50
? new MyDel(program.PrintLow)
: new MyDel(program.PrintHigh);
//執(zhí)行委托
del(randomvalue);
Console.ReadKey();
}
}
}
從上例可以看出,使用委托的首先得通過關(guān)鍵字delegate聲明一個委托類型,這個委托類型包括返回值、名稱、簽名;當(dāng)類型聲明好以后,需要通過new來創(chuàng)建委托對象,創(chuàng)建對象時的參數(shù)是一個方法,這個方法的簽名和返回類型必須與該委托類型定義的簽名一致;調(diào)用委托時,直接通過實例化的委托對象名,并提供參數(shù)即可,然后委托會執(zhí)行在其所持有的方法
委托與類
委托和類一樣,是一種用戶自定義的類型;不同的是類表示的是數(shù)據(jù)和方法的集合,而委托持有一個或多個方法,以及一系列預(yù)定義操作
委托的使用步驟
- 聲明一個委托類型
- 使用該委托類型聲明一個委托變量
- 創(chuàng)建委托類型的對象,把它賦值給委托變量;委托對象中包括指向某個方法的引用,此方法和委托類型定義的簽名與返回類型需要一致
- 增加更多的方法(可選)
- 像調(diào)用方法一樣調(diào)用委托(委托中的包含的每一個方法都會被執(zhí)行)
delegate的原則
delegate相當(dāng)于一個包含有序方法列表的對象,這些方法都具有相同的簽名和返回類型
方法的列表稱為調(diào)用列表
委托保存的方法可以來自任何類或結(jié)構(gòu),只要它們在以下兩點匹配:
- 委托的返回類型
- 委托的簽名(包括ref和out修飾符)
調(diào)用列表中的方法可以是靜態(tài)方法也可以是實例方法
在調(diào)用委托的時候,會調(diào)用列表中的所有方法
聲明委托類型
如下,delegate關(guān)鍵字開關(guān),然后是返回類型,再定義名稱與簽名
delegate void MyDel(int vallue);
返回類型與簽名指定了委托接受的方法形式
注意:委托類型是沒有方法主體的
創(chuàng)建委托對象
使用new運算符創(chuàng)建對象
MyDel del = new MyDel(object.Func); //object.Func是個實例方法 Mydel _del = new MyDel(Object.Func); //Object.Func是個靜態(tài)方法
使用快捷語法創(chuàng)建對象
MyDel del = object.Func; //object.Func是個實例方法 Mydel _del = Object.Func; //Object.Func是個靜態(tài)方法
這種語法是能夠工作是因為在方法名稱和其相應(yīng)的委托類型之間存在隱式的轉(zhuǎn)換
創(chuàng)建委托對象后會將指定的方法加入到委托的調(diào)用列表中
由于委托是引用類型,可以通過賦值來改變包含在委托變量中的引用,如下:
MyDel del; del = new MyDel(object.FuncA); //創(chuàng)建第一個對象 del = new MyDel(object.FuncB); //創(chuàng)建第二個對象
由于第二個對象也賦值給了變量del,因此del所引用的第一個對象將被垃圾回收器回收
組合委托
//創(chuàng)建兩個委托 MyDel del_A = new MyDel(object.FuncA); Mydel del_B = new MyDel(object.FuncA); //組合委托 MyDel del_C = del_A + del_B;
當(dāng)將del_A與del_B通過+進(jìn)行組合后,會返回一個新的委托對象,該對象將del_A與del_B中的方法調(diào)用列表組合到新的對象里,該新對象賦值給變量del_C,所以執(zhí)行del_C的時候,會執(zhí)行del_A與del_B中所保存的方法object.FuncA和object.FuncA
委托添加多個方法
MyDel del = object.FuncA; //創(chuàng)建并初始化委托對象 del += object.FuncB; //增加方法 del += object.FuncC; //增加方法
通過+=符號為委托對象添加更多方法,上例中,del對象不保存了三個方法,在執(zhí)行del時,這三個方法會被依次調(diào)用
注意,在使用+=為委托對象添加新的方法時,實際上是創(chuàng)建了一個新的委托對象(原對象的副本)
移除委托方法
del -= object.FuncB; //移除方法 del -= object.FuncC; //移除方法
通過-=來將委托調(diào)用列表中已保存的方法,移除動作是從調(diào)用列表的最后一個方法開始匹配,一次只會移除一條匹配的方法,如果調(diào)用列表中不存在該方法,則沒有任何效果;如果試圖調(diào)用一個空的委托則會發(fā)生異常
注意,在使用-=為委托對象移除方法時,實際上是創(chuàng)建一個新的委托對象(原對象的副本)
調(diào)用委托
調(diào)用委托就像調(diào)用方法一樣
示例:MyDel類型參考上面的定義
MyDel del = object.FuncA; //創(chuàng)建并初始化委托對象 del += object.FuncB; //增加方法 del += object.FuncC; //增加方法 //調(diào)用委托 del(55);
參數(shù)55會在調(diào)用委托對象時依次傳遞給保存的方法
一個完整的委托示例代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
class Program
{
//定義委托類型
delegate void PrintFunction(string txt);
//測試類中定義三個方法
class Test
{
public void PrintA(string txt)
{
Console.WriteLine("printA:{0}", txt);
}
public void PrintB(string txt)
{
Console.WriteLine("printB:{0}", txt);
}
public static void PrintC(string txt)
{
Console.WriteLine("printC:{0}", txt);
}
}
static void Main(string[] args)
{
Test test = new Test();
PrintFunction pf;
//實例化并創(chuàng)建委托對象
pf = test.PrintA;
//為委托對象增加方法
pf += test.PrintB;
pf += Test.PrintC;
pf += test.PrintA; //添加一個重復(fù)的方法
//通過與null比較,確認(rèn)委托對象中保存了方法
if (pf != null)
pf("Hello");
else
Console.WriteLine("pf是個空委托!");
Console.ReadKey();
}
}
}
調(diào)用帶有返回值的委托
如何委托有返回值,并且調(diào)用列表中有一個以上的方法,那么將使用最后一個方法的返回值,之前方法的返回值被忽略
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
class Program
{
//定義委托類型
delegate int DelFunction();
//測試類中定義三個方法
class Test
{
int IntValue = 0;
public int FuncA()
{
return IntValue += 1;
}
public int FuncB()
{
return IntValue += 10;
}
}
static void Main(string[] args)
{
Test test = new Test();
DelFunction df;
df = test.FuncA;
df += test.FuncB;
//最終返回值的是11
if (df != null)
Console.WriteLine("返回值:"+df());
else
Console.WriteLine("pf是個空委托!");
Console.ReadKey();
}
}
}
具有引用參數(shù)的委托
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
//定義委托類型
delegate void MyDel(ref int x);
class Program
{
static void Add1(ref int x) { x += 1; }
static void Add2(ref int x) { x += 2; }
static void Main(string[] args)
{
Program program = new Program();
MyDel del = Add1;
del += Add2;
//ref會將x當(dāng)作引用值傳遞給委托方法
int x = 5;
del(ref x);
Console.ReadKey();
}
}
}
在調(diào)用Add1方法時,x = 5+1,再調(diào)用Add2方法時,不是x = 5+2而是x = 6 +2
參考:ref按引用傳遞參數(shù)
在方法的參數(shù)列表中使用 ref 關(guān)鍵字時,它指示參數(shù)按引用傳遞,而非按值傳遞。 按引用傳遞的效果是,對所調(diào)用方法中參數(shù)進(jìn)行的任何更改都反映在調(diào)用方法中。 例如,如果調(diào)用方傳遞本地變量表達(dá)式或數(shù)組元素訪問表達(dá)式,所調(diào)用方法會替換 ref 參數(shù)引用的對象,然后,當(dāng)該方法返回時,調(diào)用方的本地變量或數(shù)組元素將開始引用新對象
匿名方法
匿名方法是在初始化委托時內(nèi)聯(lián)聲明的方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
//定義委托類型
delegate void MyDel(ref int x);
class Program
{
static void Add1(ref int x) { x += 1; }
static void Add2(ref int x) { x += 2; }
static void Main(string[] args)
{
Program program = new Program();
//采用匿名方法形式代替具名方法
MyDel del = delegate(ref int y) { y += 3; };
del += Add1;
del += Add2;
//ref會將x當(dāng)作引用值傳遞給委托方法
int x = 5;
del(ref x);
Console.ReadKey();
}
}
}
在聲明委托變量時作為初始化表達(dá)式,或在為委托增加事件時使用
語法解析
以關(guān)鍵字delegate開頭;后跟小括號提供參數(shù);再后跟{}作為語句塊
delegate (Parameters) {ImplementationCode}
- 匿名方法不會顯示的聲明返回類型delegate (int x) { return x;}即為返回一個int類型的值
- 參數(shù)的數(shù)量、位置、類型、修飾符必須與委托相匹配
- 可以通過省略圓括號或使圓括號為空來簡化匿名方法的參數(shù)列表,前提是參數(shù)是不包含out參數(shù),方法體中不使用任何參數(shù)
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
//定義委托類型
delegate void MyDel(ref int x);
class Program
{
static void Add1(ref int x) { x += 1; }
static void Add2(ref int x) { x += 2; }
static void Main(string[] args)
{
Program program = new Program();
//采用匿名方法形式代替具名方法
MyDel del = delegate(ref int y) { y += 3; };
del += Add1;
del += Add2;
//匿名方法未使用任何參數(shù),簡化形式
del += delegate{int z = 10;};
//ref會將x當(dāng)作引用值傳遞給委托方法
int x = 5;
del(ref x);
Console.ReadKey();
}
}
}
如果定義一個帶有params形式的參數(shù),在使用匿名方法的時候可以省略params關(guān)鍵字以簡化代碼
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeForDelegate
{
//定義一個帶有params形式參數(shù)的委托類型
delegate void DelFunction(int x, params int[] z);
class Program
{
static void Main(string[] args)
{
Program program = new Program();
// 關(guān)鍵字params被忽略(省略關(guān)鍵字以簡化)
DelFunction df = delegate(int x, int[] y) { ... };
Console.ReadKey();
}
}
}
Lambda表達(dá)式
Lambda可以簡化匿名方法,語法形式如下:
(參數(shù)) => {語句塊} // => 讀作 gose to
參數(shù)中的類型可以省略
如果只有一個參數(shù),圓括號可以省略
如果沒有參數(shù),圓括號不可以省略
語句塊如果只有一行代碼,花括號可以省略
示例:
MyDel del = delegate(int y) { return y += 3; }; //匿名方法
MyDel del1 = (int y) => {return y += 3;} // Lambda表達(dá)式
MyDel del2 = (y) => {return y += 3;} // 省略參數(shù)類型
MyDel del3 = y => y += 3; // 省略圓括號和花括號,雖然沒有return,但仍會返回y的值
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對我們的支持。
上一篇:C#實現(xiàn)斐波那契數(shù)列的幾種方法整理
欄 目:C#教程
本文標(biāo)題:C#沉淀之委托的深入講解
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/5075.html
您可能感興趣的文章
- 01-10C#一個簡單的定時小程序?qū)崿F(xiàn)代碼
- 01-10微信開放平臺之網(wǎng)站授權(quán)微信登錄功能
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量二
- 01-10C#編程自學(xué)之開篇介紹
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量三
- 01-10C#編程自學(xué)之運算符和表達(dá)式
- 01-10C#編程自學(xué)之類和對象
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量一
- 01-10C#編程自學(xué)之流程控制語句
- 01-10C#基于委托實現(xiàn)多線程之間操作的方法


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機閱讀
- 01-10C#中split用法實例總結(jié)
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-11ajax實現(xiàn)頁面的局部加載
- 04-02jquery與jsp,用jquery
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改


