c#學習筆記之委託
委託
最近自己在除錯C#專案,發現經常可以看到委託和lambda
表示式,各種花裡胡哨的寫法把我給整的雲裡霧裡的,於是自己特意花了一點功夫來整理關於delegate
的相關知識,方便自己日後查閱。
何為委託
委託是.NET中的定址方法,和C++的
函式指標
很像;但是委託是型別安全的類
,定義了返回型別和引數型別,也就是說委託一種使用者自定義的型別,和普通的類一樣;
委託的使用
宣告委託
delegate void IntMethodInvoker(int x); //定義委託IntMethodInvoker, 引數是int,返回型別void
我們可以把委託當做是一件事情,就是給方法名字和返回型別指定一個
別名
從上面的例子可以總結出宣告委託的模板如下:
delegate
+ 返回型別 + 委託型別名 + 引數列表
委託的使用
例項化委託,並且需要對其進行初始化,這樣可以類似地當做一個變數使用了,下面給出一個具體的例子來說明如何使用委託。
using System; namespace DelegateSamples{ delegate double DoubleOp(double x); // 宣告一個委託型別 class MathOperations { public static doube MultiplyByTwo(double value) return value * 2; public static double Square(double value) return value * value; } class Program { static void Main() { // 宣告一個委託陣列,就像普通陣列一樣 DoubleOp[] operations = {MathOperations.MultiplyByTwo, MathOperations.Square}; for(int i = 0; i < operations.length; i++) { Console.WriteLine("using operations[{0}]:", i); ProcessAndDisplayNumber(operations[i], 3.2); Console.WriteLine(); } } static void ProcessAndDisplayNumber(DoubleOp action, double value) { double res = action(value); // 呼叫action實際封裝的方法 Console.WrireLine("Value is {0}, result of operation is {1}", value, res); } }
在呼叫委託的時候,最後判斷委託是否為
null
,不然可能會引起異常
Action和Func委託
Action<T>
委託表示引用一個void返回型別的方法,Action<T>
有很多種變體,Action<in T>
呼叫帶一個引數的方法,沒有泛型引數的Action類呼叫不帶引數的方法;
Func<T>
委託類似,表示引用帶返回型別的方法,Func<T>
也有很多變體,Func<Out TResult>
表示呼叫帶返回型別但沒有引數的方法,Func<in T, out TRsult>
呼叫一個帶引數的方法,以此類推;
// 宣告一個返回double型別,並且帶一個double引數的委託 Func <double, double> operations = {MathOperations.MultiplyByTwo, MathOperations.Square}; // 注意這個方法和上面方法的不一樣,利用了Func類 static void ProcessAndDisplayNumber(Func <double, double> action, double value) { double res = action(value); // 呼叫action實際封裝的方法 Console.WrireLine("Value is {0}, result of operation is {1}", value, res); }
多播委託
在之前的例子中,每個委託都只含有一個方法呼叫,也就是說呼叫多少次委託就是呼叫多少次方法。
但是,一個委託也可以包含多個方法,這種委託叫做
多播委託
,因此,這種委託必須返回void
,不然返回的資料是不對的
Action<double> operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square; //這裡在給委託新增一個方法
對於多播委託的使用,呼叫方法鏈的順序並沒得到正式定義,這要求我們儘量避免依賴特定順序呼叫方法的程式碼;
從上面的例子可以知道,多播委託是可以識別+,+=,-,-=這些符號的,下面給出具體的示意:
- +=: 表示委託新增一個方法
- -=: 表示委託減少一個方法
匿名方法
在上面的例子中,委託所呼叫的方法,都是我們自己預先寫好的,但是委託也可以使用匿名方法,也就是將匿名方法用作委託的引數,這在例項化委託的會有一些不一樣的,具體如下面例子所示:
string name = "";
// Func<string, string>委託接受一個string引數,返回一個string,注意匿名方法的實現,delegate開頭
Func<string, string> anonDel = delegate(string param){
param += name;
param += "just for test";
return param;
};
使用匿名方法需要遵守的規則:
- 在匿名方法中不能使用跳轉語句(goto、break、continue),匿名方法外部的語句也不能跳轉到匿名方法內部;
- 在匿名方法中不能訪問不完全的程式碼,也不能訪問在匿名方法外部定義的ref、out引數;
在C#3.0之後,lambda表示式代替了匿名方法,寫起來感覺更舒服;
Lambda表示式
lambda表示式主要是用來替代匿名方法的,因為顯然委託知道他自己需要呼叫的是什麼方法,不需要宣告delegate
關鍵字,在引數和方法體之間插入=>
,表示“goes to",具體示例如下所示:
string name = "";
// Func<string, string>委託接受一個string引數,返回一個string,這裡使用lambda表示式
Func<string, string> anonDel = param => {
param += name;
param += "just for test";
return param;
};
// 這樣的lambda表示式是不是很優雅,就是除錯程式的時候會有點煩
為了更好地使用lambda表示式,方便我們自己寫程式碼寫的花裡花哨的,就對其多做一點介紹:
引數
如果只有一個引數,就只需要寫出引數名字就行
Func<string, string> oneParam = s => String.Format("change to Upper {0}", s.ToUpper());
使用多個引數,就把引數用寫在()裡
Func<double, double, double> twoParams= (x, y) => x * y; // 返回x*y
在使用多個引數的時候,我覺得還是在引數前面加上型別比較好,便於理解,例如(double x, double y) => x * y;
多行程式碼
只有一條語句,方法裡面不需要{}和return,編譯器會自動新增一個隱式的return
Func<double, double, double> result = x, y => x * y;
含有多條語句,需要加上{}和return
string lastname = "Alex"; Func<string ,string> printName = name => { name += lastname; return String.Format("Being Upper: {0}",name); }