委託和lambda表示式,Action和Func
1、為什麼要用委託
我們為什麼要有委託?任何東西存在即合理,不合理的也會被時間淘汰掉,委託既然存在肯定有存在的必要,我們來看一下什麼時候可以用到委託。
接下來我們有個需求,就是呼叫一個方法,取出1-1000個數字中所有是10的倍數的數字
public static List<int> GetNum() { List<int> lst = new List<int>(); //這個演算法是最簡陋的,但是舉這個例子是最合適的 for (int i = 1; i < 1000; i++) { if (i%10==0) { lst.Add(i); } } return lst; }
這個很好寫,但是如果有一天,我們的需求變了,想取出1-1000個數字中所有是8的倍數的數字,那我們應該怎麼寫?是不是隻要將if裡面的條件改為i%8==0就行了,但是有一點變動的話就修改方法,說明這個方法寫的並不好,如果方法很複雜的話修改也很艱難。可能有人會說在新增一個獲取是8的倍數的方法,這樣的話會增加資料冗餘,也就是重複的內容。
如果我們可以傳遞方法的話,只要傳過來一個(有一個引數為int型別返回值為Boolean型別的方法),這個問題將不是問題
public static List<int> GetNum(有一個int型別的引數返回值為Boolean型別的方法) { List<int> lst = new List<int>(); for (int i = 1; i < 1000; i++) { if (有一個int型別的引數返回值為Boolean型別的方法) { lst.Add(i); } } return lst; }
是不是我們將想要獲取什麼樣的數字這個操作來交給了呼叫者,無論呼叫者想獲取1-1000以內什麼樣的數字都可以完成。但是這個方法的前提就是可以傳遞方法,這個時候我們就需要用到委託了。
2、委託如何使用
委託其實就是一個能夠指向方法的 指標,定義了一個委託就是定義了一個 型別
首先我們先來定義一個委託,也就是定義一個型別
//訪問修飾符 委託關鍵字 方法的返回值型別 要定義的型別名(引數1,引數2.....); public delegate Boolean DelegateFunc(int x);
委託定義好了,也就是說我們已經定義好了一個DelegateFunc型別,這個型別的使用方法就和public class DelegateFunc{}寫了一個類是一樣的,因為都是定義了一個型別,既然大家都是型別,那用法肯定都是一樣的,我們先來看一下宣告普通的型別是如何宣告的
//型別 變數名 = 例項化一個Object型別的物件(建構函式有無引數) Object obj = new Object();
而DelegateFunc既然也是我們定義好的一個型別,那用法自然一樣(因為建構函式需要引數,所以下面這樣寫是不對的,沒有傳入引數)
//型別 變數名 = 例項化一個DelegateFunc型別的物件(建構函式有無引數) DelegateFunc func = new DelegateFunc();
委託是一個能夠指向方法的指標,而它的建構函式就是需要一個方法,接著我們來定義一個返回值為Boolean,能接收一個int型別引數的方法
//相當於方案1 public static Boolean Condition1(int i) { //模擬複雜的操作 相當於return i%10==0; int x = i % 10; if (i % 10 == 0) { return true; } else { return false; } }
定義好了方法我們再來例項化一個DelegateFunc型別的物件
//建構函式放入方法的話不需要帶(),帶()的話是呼叫 DelegateFunc func = new DelegateFunc(Condition1);
下面看一下下面這種宣告委託型別的方式
//同樣都是型別Object型別可以這樣寫 //因為String最終是繼承自Object,並且String可以預設轉換為Object Object obj = "obj"; //而DelegateFunc也可以通過這種方式賦值,這說明Condition1可以預設轉換為委託型別 DelegateFunc func = Condition1;
例項化完成之後func變數就會指向Condition1方法,呼叫方式如下
//呼叫委託型別的物件和呼叫普通的方法是一樣的 func(10);
然後我們把剛才寫的GetNum方法修改為如下的樣子,引數為接收一個DelegateFunc型別的引數,也就是委託型別
public static List<int> GetNum(DelegateFunc func) { List<int> lst = new List<int>(); for (int i = 1; i < 1000; i++) { //呼叫傳過來的方法,根據呼叫者傳過來的方法拿到想要的數字 if (func(i)) { lst.Add(i); } } return lst; }
在Main方法中呼叫GetNum方法
//宣告委託 DelegateFunc func = new DelegateFunc(Condition1); //呼叫方法 List<int> lst = GetNum(func); //也可以直接呼叫,因為都會預設轉換 //List<int> lst = GetNum(Condition1); //輸出 foreach (int item in lst) { Console.WriteLine(item); }
輸出
如果我們有新的方案的話,只需要在新建一個方案,然後傳入方法中,比如我們還看剛才那個求1-1000以內8的倍數,我們只需要宣告一個新的方案
//宣告一個新的方案2 public static Boolean Condition2(int i) { //同樣模擬複雜的操作 相當於return i%8==0; int x = i % 8; if (i % 8 == 0) { return true; } else { return false; } }
然後和剛才一樣,宣告一個委託就行,傳入GetNum就行。
不過我們看一下,雖然已經比較簡化程式碼了,但是寫起來還是很麻煩,然後我們來看一下lambda表示式
3、lambda表示式
首先lambda表示式只是方法的一種寫法!lambda宣告的方法是匿名方法,它和委託並不是綁死的,這是兩個東西,但是lambda表示式和委託結合使用是非常常見的!
看一下lambda表示式的語法,也就是函式的另一種寫法
//可以這樣寫 //DelegateFunc func = new DelegateFunc((i) => //{ // return i % 8 == 0; //}); //也可以這樣 不進行new DelegateFunc操作,因為會預設轉換 //如果有多句的話,這樣寫每一句通過分號隔開 DelegateFunc func = (i) => { return i % 8 == 0; }; //如果只有單句 不用寫return 預設reuturn i%8==0 這一句計算出來的值 func = (i) => i % 8 == 0; //如果只有一個引數 多個引數的話要(i,j,....)這樣寫 func = i => i % 8 == 0;
除此之外lambda也可以用來宣告方法,貌似只能寫一句,,,,
//無返回值 public static void HelloWord() => Console.WriteLine("Hello Word!"); //有返回值 public static String GetHelloWord() => "Hello Word!";
有了lambd我們再來呼叫GetNum,就會變得非常方便
//8的倍數 List<int> lst = GetNum((i) => i % 8 == 0); //10的倍數 lst = GetNum(i => i % 10 == 0); //20的倍數 lst = GetNum(i => i % 20 == 0);
4、Action和Func
Action和Func是微軟已經定義好的的兩種委託型別,區別是Action是沒有返回值的,而Func是需要返回值的
Action使用方法
//無引數 Action action1 = () => Console.WriteLine("action"); //有引數的話呼叫Action<T> Action<int> action2 = (i) => Console.WriteLine(i); //多個引數就在生命的時候<T,T,T> Action<int,string> action3 = (i, str) => Console.WriteLine(i+"\t"+str); //呼叫 action1(); action2(10); action3(10,"s");
執行結果:
Func使用方法
//Func是沒有Func型別的,只有Func<T>型別 Func<string> func1 = () => "func"; //如果需要引數 Func<T,T,T> //最後一個T型別為返回值型別,前面的全都為引數的型別!!! Func<string, int> func2 = (i) => int.Parse(i); //如果有多個引數 最後一個T型別為返回值型別,前面的全都為引數的型別!!! Func<int, string, int> func3 = (i, str) => int.Parse(i + str); //呼叫 Console.WriteLine(func1()); Console.WriteLine(func2("123")); Console.WriteLine(func3(1,"23"));
執行結果
&n