1.委托的定義
【定義】:委托是C#中函數回調機制,就是c語言中的函數指針在面向對象中的封裝;簡而言之就是函數指針。
它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞。
委托和類相似,都是用戶自定義的一種類型,只不過類表示的數據的集合,而委托表示的是一個或多個方法
【理解】:類比聲明一個字符類型的變量“string name ”,其中string 是一個類,定義了name參數所能代表的值的種類,也就是name參數的類型。
【作用與意義】:
這種將方法動態地賦給參數的做法,可以避免在程序中大量使用If-Else(Switch)語句,同時使得程序具有更好的可擴展性。
2.委托的聲明
【使用方法】:
關鍵字 delegate
委托和所引用的方法必須保持一致:
1、參數個數、類型、順序必須完全一致。
2、返回值必須一致。
3. 委托的實例
//聲明一個委托指向一個函數,注意聲明在同一個命名空間
public delegate void DelSayhi(string name);
class Program
{
static void Main(string[] args)
{
//---------------------------------------------------------------------法1
//DelSayhi del=new DelSayhi (SayhiChinese );
//☆注意函數名直接賦值給委托,不需要在函數名之后加括號
//Sayhi("志銘", del);
//委托就是為把一個函數當參數,但這樣不明顯
//---------------------------------------------------------------------法2
//可以直接把一個函數給委托
//DelSayhi del = SayhiChinese;
//☆注意函數名直接賦值給委托,不需要在函數名之后加括號
//Sayhi("志銘", del);
//---------------------------------------------------------------------法3
//既然可以法2 ,那干脆直接如下,這樣你就可以發現函數作為了參數
//Sayhi ("志銘", SayhiChinese);
//---------------------------------------------------------------------多播委托
//委托可以將多個方法賦給同一個委托,或者叫將多個方法綁定到同一個委托,
//這就是——多播委托
//當調用這個委托的時候,將依次調用其所綁定的方法。在這個例子中,語法如下
//DelSayhi del=new DelSayhi (SayhiChinese );
//del += SayhiEnglish;//使用+=給此委托變量再綁定一個方法
//Sayhi("志銘", del);
//---------------------------------------------------------------------取消綁定
//既然給委托可以綁定一個方法,
//那么也應該有辦法取消對方法的綁定,很容易想到,這個語法是“-=”:
//DelSayhi del = new DelSayhi(SayhiChinese);
//del += SayhiEnglish;//使用+=給此委托變量再綁定一個方法
//Sayhi("志銘", del);
//del -= SayhiChinese;//使用-=給此委托變量取消綁定一個方法
//Sayhi("志銘", del);
//不使用SayHi函數,在新建了了委托對象后直接使用委托作函數
//注:當然我們知道可以在這里直接使用SayhiChinese和SayHiEnglish倆個函數,我們在這里就是舉例子
DelSayhi del = new DelSayhi(SayhiChinese);
if (null != del)//注意調用委托的時候一定要先判斷委托是否為空,為空時會拋異常
{
del("志銘");//其實是del.Invoke("志銘")的簡寫
}
Console.ReadKey();
}
//Sayhi這個函數就是為了把下面兩個函數SayhiChinese和SayhiEnglish統一起來
//我想在這個函數中調用這兩個函數,也就是有一個函數作為參數,但具體哪一個不確定
//所以我定義了一個委托DelSayhi指向這兩個函數
public static void Sayhi(string name, DelSayhi del)
{
del(name);
}
public static void SayhiChinese(string name)
{
Console.WriteLine("你好" + name);
}
public static void SayhiEnglish(string name)
{
Console.WriteLine("Hello" + name);
}
}
4.委托的注意細節
如果委托有返回值並且在調用列表中有一個以上的方法(即多播委托),會發生下面的情況:
- 調用列表中最后一個方法返回的值就是委托調用返回的值。
- 調用列表中所有其他方法的返回值都會被忽略(忽略不是不執行)。
例如
delegate int mypel();//聲明有返回值的方法
class MyClass
{
int IntValue 5;
public int Add2(){ Intvalue +=2;return IntValue;}
public int Add3(){ IntValue +=3;return IntValue;}
}
class Program
{
static void Main()
{
MyClass mc=new MyClass();
MyDel mDel=mc.Add2;//創建並初始化委托
mDel +=mc.Add3//增加方法
mDel +=mc.Add2//增加方法
Console.Writeline("value:{0}",mDe1());調用委托並使用返回值
}
}
結果:value:12;
5.泛型委托
(詳見《精通C#》--10.4泛型委托)
.net中內置類兩個委托類型Action<>和Func<>,用法和自己定義的委托一樣,只是不需要我們自己聲明。
建議使用委托的時候就先考慮這兩種委托。
5.1.Action<>委托
Action<>類型的委托指向--最多16個參數並返回值為void的方法。
class Program
{
static void Main(string[] args)
{
Action<string> del=SayhiChinese;//定義一個Action<string>委托,指向的方法返回值為void,有一個string類的參數。
//你想想,你之前使用委托是不是要要自己聲明一個委托類型了
//這里就相當於.net已經幫我們 delegate void Action<T>(T p)
if (null != del)
{
del("志銘");
}
Console.ReadKey();
}
public static void SayhiChinese(string name)
{
Console.WriteLine("你好" + name);
}
public static void SayhiEnglish(string name)
{
Console.WriteLine("Hello" + name);
}
}
5.2.Func<>委托
Func<>類型的委托指向--最多16個參數且有返回值的方法。
有一點要注意的就是,類型列表的順序:Func<>最后的一個類型參數是返回值的類型
show your code:
class Program
{
static void Main(string[] args)
{
Func<int,int,string> del=SumToString;
//注意類型參數列表中的類型順序,最后一個string 是函數返回值的類型
//這里就相當於.net已經給我們 delegate RT Func<T1,T2,RT>(T1 x,T2 y)
Console.WriteLine(del(1,2));
}
public static string SumToString(int x,int y)
{
return (x+y).ToString();
}
}
6.委托的意義
-
在代碼結構的設計上使用委托可以降低類與類之間的耦合性。
有兩個類AClass和BClass,現在當某個條件得到滿足的時候,AClass中的方法AFunc調用BClass中的方法BFunc()。
代碼如下:
public class Program
{
AClass AObj = new AClass();
void Main()
{
AObj.AFunc();
}
}
public class AClass
{
BClass BObj=new BClass();
public void AFunc()
{
//當某個條件滿足時
if(true)
{
BObj.BFunc();
}
}
}
public class BClass
{
public void BFunc()
{
}
}
這樣的寫法使得A類和B類之間的關系為強耦合的關系,B的變化會在很大的幾率上影響到A,我們將這樣的關系稱之為不穩定的關系,這是面向對象編程設計所不提倡的,那么如何將不穩定的關系變為穩定的關系呢?方法有多種,委托的應用就是其中一種。
代碼如下:
//聲明一個委托類型,它能接受的函數類型是沒有返回也沒有參數
public delegate void MyDelegate();
public class AClass
{
//定義一個委托類型的變量 ,變量名字為 mydelagate
public MyDelegate mydelagate = null;
public void AFunc()
{
//當某個條件滿足時
if(true)
{
if(mydelagate != null)
{
mydelagate();
}
}
}
}
public class BClass
{
public void BFunc()
{
}
}
public class Pragrom
{
AClass AObj = new AClass();
BClass BObj = new BClass();
void Main()
{
AObj.mydelagate = BObj.BFunc();
AObj.AFunc();
}
}
可以看到,只需要將B類中的方法通過公開的委托注冊到A類去執行,這樣就避免了A類和B類之間的相互引用,提高了類關系之間的穩定性。
其實早期的delegate
是翻譯成代表
,我們是可以這樣理解的,委托是被調用函數的代表,函數調用者調用委托,委托調用其代表的函數,這用就形成了一種間接調用,從而實現調用者和被調用函數的解耦!
7.匿名方法
委托的初始化是對其賦值一個方法,但是有時候這個方法就只在初始化這個委托的時候使用,我們沒有必用去完整的聲明一個方法,所以在C#2.0中引入了匿名方法(anonymous method)的概念。
- 匿名方法的語法
- 使用
delegate
關鍵字 - 不需要標注返回值類型(若是匿名方法有返回值,其類型要符合相應委托定義的類型)
- 參數列表和普通方法聲明的格式一樣,若是沒有參數則不需要寫(包括參數列表的括號,不省略圓括號也是可以的)
如下:
delegate (Parameters){方法體...}//注意若是沒有參數則省略參數列表(Parameters)
- 實例:
delegate void Del();
static void Main()
{
Del del=delegate
{
Console.WriteLine("Hello world ");
}
del();
}
8.Lambda表達式
C#2.0中引入匿名方法,但是語法依舊有點麻煩,C#3.0中引入Lambda表達式
Lambda表達式使用=>
符號編寫,讀作goes to
見匿名方法到Lambda表達式的演變過程如下:
public delegate int Del(int x);
Del del1 = delegate(int x) {return x+1;};//匿名方法
Del del2= (int x) => {return x+1;};//Lambda表達式
Del del3= (x) => {return x+1;}
Del del4= x => x+1;
【注意】
若是沒有參數則Lambda表達式的參數使用一個空括號表示()
。
若是函數體中有多句代碼,大括號不可以省略。
注意參數的圓括號只有是參數只有一個並且是隱式類型的才可以省略,否則不可以省略,見上面代碼的del2