0x01定義一個委託,相當於定義一個可以存儲方法的特殊變量類型
下面我們看具體的代碼,通過代碼更好理解
delegate void IntMethodInvoker(int x);
這行代碼就是聲明一個委託,其中delegate
是關鍵字,表示聲明一個委託,void
是要存儲的方法的返回類型,IntMethodInvoker
是聲明的委託類型名字,結合最開始的那句話就是自定義的類書變量類型名,int x
則是要存儲的方法的返回類型
這樣一個委託可以存放哪些方法呢?
只要返回類型一致,參數列表一致的方法都能存儲
在這個例子中,就是返回類型爲void,有一個int形參的方法
void test(int t)
{
Console.WriteLine("Just a test" + t )
}
如這樣的test方法就可以存儲到IntMethodInvoker
委託的實例中
委託是一種特殊類型的對象,定義好委託之後就可以創建它的實例瞭然後存儲方法
IntMethodInvoker DelegateTest = test;
這行代碼即將test方法的地址存放到IntMethodInvoker
委託的實例DelegateTest
中,在以後只需要調用DelegateTest
就能執行test方法。
DelegateTest(32);
這行代碼與test(32)
效果相同
委託可以添加多個方法引用,添加多個方法引用的委託叫多播委託
添加多個方法引用使用+=,若要刪除添加的方法引用使用-=
0x02使用委託可以將方法當作參數傳遞
既然前面說了委託類似於定義一個可以存儲方法的特殊變量類型,那麼我們也可以將這個特殊類型作爲方法的形參,將一個方法引用添加到委託實例中,然後將委託實例作爲另一個接受委託類型參數的方法的實參,就達到了將方法傳遞給另一個方法的效果
class Program
{
static void Main(string[] args)
{
Test test = new Test();
Test.TestDelegate testDelegate = new Test.TestDelegate(test.Fun);
test.KK(testDelegate);
}
}
class Test
{
public delegate void TestDelegate();
public void Fun()
{
Console.WriteLine("這是一個簡單的方法!");
}
public void KK(TestDelegate testDelegate)
{
Console.WriteLine("這是一個接收委託的方法");
testDelegate();
}
}
這是一個將委託作爲參數,將一個方法傳遞給另一個方法的例子
在這個例子中,先聲明瞭一個Test實例,然後聲明瞭一個TestDelegate
委託實例testDelegate
testDelegate
委託包含了test.Fun()
的引用,這時並沒有調用test.Fun()
,只是保存了引用
接下來執行test.KK(testDelegate)
,這時testDelegate
委託作爲形參傳遞到了KK方法中
KK方法在調用testDelegate();
時,委託執行Fun方法
所以最後的執行結果是
這是一個接收委託的方法
這是一個簡單的方法!
0x03Func<T>
和Action<T>
委託非常常用,所以.Net內置了兩個委託類型,這樣我們就不用自己去聲明委託了
Action<T>
泛型委託,表示引用一個void返回類型的方法。這個委託存在不同的變體,可以接受需要0 ~ 16個參數的方法
Func<T>
泛型委託,與Action<T>
相似,但是表示引用一個有一個返回值類型的方法。Func<T>
同樣可以接收0 ~ 16個參數的方法,默認最後一個是返回類型,即Fun<T>
最多有十七位參數
Func<string,string,int>
這個Func表示可以接收一個參數列表爲兩個string,返回一個int的方法。
0x04Lambda和匿名方法
匿名方法:
Func<string, string> anonDel = delegate (string param)
{
param += mid;
param += " and this was added to the string.";
return param;
};
匿名方法的作用:
委託是存儲方法的引用,也就是說,要用委託,就必須要先有方法,但是實際上有很多時候委託所存儲的方法引用只會在委託或者事件(事件基於委託)中使用,這時候我們單獨定義一個方法很麻煩,這時候就可以使用匿名方法,上方就是一個匿名方法,我們看右邊
delegate (string param)
{
param += mid;
param += " and this was added to the string.";
return param;
};
這個就是匿名方法,使用delegate
關鍵字,如果我們把delegate
換成一個普通的名字
KK (string param)
{
param += mid;
param += " and this was added to the string.";
return param;
};
這就是一個普通的方法(結尾的分號是Fun委託賦值語句的結尾),所以匿名方法就是一個普通的方法將方法名去掉換成delegate,以達到簡化代碼的目的
Func<string, string> anonDel =
delegate (string param)
{
param += mid;
param += " and this was added to the string.";
return param;
};
這樣看是不是很好理解了
Lambda表達式:
在我的理解中,Lambda表達式其實是爲了簡化匿名方法
我們依然用上面的代碼
//匿名方法
delegate (string param)
{
param += mid;
param += " and this was added to the string.";
return param;
}
//Lambda表達式
(string pram) =>
{
param += mid;
param += " and this was added to the string.";
return param;
}
這兩個是等價版本, **=>**就是Lambda運算符,現在我們看它的兩邊是什麼,左邊是參數列表,右邊是方法體。
這個Lambda其實有一點複雜了,通常我們遇到的Lambda會更簡化
- 如果只有一個參數,只需要參數名就足夠了,即這個Lambda中,可以將(string pram)改寫爲pram
- 如果方法體只有一行語句,那麼在方法塊中就不需要花括號和return語句
舉個例子
Func<double,double> square = (double x) =>
{
return x*x;
};
這個Lambda表達式可以簡寫爲
Func<double,double> square = x => x*x
Lambda表達式的閉包問題
這裏就一句話,在Lambda表達式使用了外部的變量(不是Lambda表達式內的變量),外部的變量的值在調用時決定,而不是定義Lambda表達式時決定
int someVal = 5;
Func<int, int> ZZ = x => x + someVal;
someVal = 7;
Console.WriteLine(ZZ(3));
最後輸出的時7,因爲ZZ委託調用Lambda表達式時someVal已經等於7了