前言
在C#我們可以自定義委托,但是C#為什么還要內置泛型委托呢?因為我們常常要使用委托,如果系統內置了一些你可能會用到的委托,那么就省去了定義委托,然后實例化委托的步驟,這樣一來既使代碼看起來簡潔而干凈又能提高程序員的開發速度,何樂不為呢!通過本文可以讓你復習擴展方法,同時可以循序漸進的了解系統內置泛型委托的實現以及委托逐步的演化過程。
?Action
概念:封裝一個方法,該方法具有五個參數并且不返回值。并且類型參數為逆變
下面我就自定義實現一個Action<T>無返回值的委托。我們同樣定義一個Person類,似乎我隨筆中永遠都離不開Person的話題,哈哈!請看如下代碼
????public?class?Person{????????public?string?Name?{?get;?set;?}????????public?int?Age?{?get;?set;?}????????public?bool?Gender?{?get;?set;?}}
然后在控制臺中通過?ForEach?方法模擬Action委托,先定義一個獲得Person的列表GetList()
????????static?List<Person>?GetList(){List<Person>?list?=?new?List<Person>()?{?new?Person(){?Name?=?"花千骨?????(女媧后人及妖神)",???Age?=?12,?Gender?=?false},????????????new?Person(){?Name?=?"白子畫?????(長留尊上)",???Age?=?13,?Gender?=?true},????????????new?Person(){?Name?=?"東方彧卿???(異朽閣主及蜀國大學士)",?Age?=?14,?Gender?=?true},????????????new?Person(){?Name?=?"輕水???????(長留弟子)",?????Age?=?15,?Gender?=?false},????????????new?Person(){?Name?=?"孟玄朗?????(蜀國皇帝及長留弟子)",???Age?=?16,?Gender?=?true}};????????????return?list;}
因為我們知道在用委托時,有這樣幾個步驟:
(1)定義委托
(2)實例化委托
(3)將方法指針添加到實例化委托對象中
?但是現在我們無需定義委托,已經有了內置委托,只需將其實例化即可,同時添加方法的指針一般是有明確的方法,如果我們只是臨時的用方法,這時就可以派匿名方法上場了,所以上面三步就可以簡化成兩步。代碼如下:
????????????var?list?=?GetList();list.ForEach(new?Action<Person>(??????????????????delegate(Person?p){Console.WriteLine(p.Name);}));
上述代碼頗有點jQuery中Each的遍歷方法的意味。結果打印出:
?我們知道ForEach這個方法里面的的參數就是?Action<T> action?,所以我們可以直接進行如下簡寫
?list.ForEach(delegate(Person?p)?{?Console.WriteLine(p.Name);?});
其打印結果和上面是一樣的。其代碼可以繼續進行更加的精簡,不著急,我們循序漸進的來。
Predicate
概念:定義一組條件并確定指定對象是否符合這些條件的方法。返回值為bool類型,并且類型參數為逆變。
用到此泛型委托莫過于List中的?FindAll()?方法了,它就是從一個集合中根據條件篩選出一個新的集合出來。上節剛好學過擴展方法,我們可以自定義實現這個方法用擴展方法加在泛型集合List上根據??Predicate?委托的參數條件進行篩選。
static?List<T>?SelfDefineFindAll<T>?(this?List<T>?list,?Predicate<T>?pre)???/*注意:既然是添加的擴展方法,在此例中控制臺的Program也要聲明為靜態類*/{List<T>?preList?=?new?List<T>;?/*根據條件篩選出的數據添加到該集合中*/????foreach?(T?t?in?list){????????????if?(pre(t))?/*根據條件進行篩選*/{preList.Add(t);}}??????return?preList; }
我們查詢出年紀大于13歲的并且根據ForEach來遍歷篩選出來的數據,代碼如下:
?var?list?=?GetList();?var?preList?=?list.SelfDefineFindAll(delegate(Person?p)?{?return?p.Age?>?13;?});preList.ForEach(delegate(Person?p)?{?Console.WriteLine(p.Name);?});
結果打印出正確的結果,如下:
而通過C#中的FindAll,則只需如下做即可同樣達到上述的效果,只是不是擴展方法罷了:?
list.FindAll(delegate(Person?p)?{?return?p.Name});
上述代碼其實可以更加精簡,不著急,我們一步一步循序漸進的來。?
Comparison
概念:表示比較同一類型的兩個對象的方法。參數類型為逆變,返回值為int。
?list.Sort(new?Comparison<Person>(delegate(Person?p,?Person?p1)?{?return?p.Age?-?p1.Age;?}));??/*年齡按照從小到大順序排列*/
同樣可以簡寫為:
?list.Sort((delegate(Person?p,?Person?p1)?{?return?p.Age?-?p1.Age;?}));
Func
貌似在系統內置泛型委托中Func在實際項目開發中是使用的最多的。
概念:封裝一個具有一個參數并返回?TResult?參數指定的類型值的方法。參數類型為逆變,返回值參數類型為協變。
用到此委托的最多的就是List泛型集合中的?Select?方法了,看看內置的Select方法是如何利用Func來實現返回一個新的集合的。
(1)結合匿名方法實現
我們接下來在上面基礎上再定義一個Animal類。屬性和Person類一樣,代碼如下:
????public?class?Animal{????????public?string?Name?{?get;?set;?}????????public?int?Age?{?get;?set;?}????????public?bool?Gender?{?get;?set;?}}
接下來我們就通過Select方法通過Person的集合來返回一個新的集合即Animal集合。代碼如下:
????????????var?list?=?GetList();List<Animal>?animalList?=?list.Select(new?Func<Person,?Animal>(delegate(Person?p){????????????????return?new?Animal()?{?Name?=?p.Name,?Age?=?p.Age,?Gender?=?p.Gender?};})).ToList();animalList.ForEach(delegate(Animal?animal)?{?Console.WriteLine(animal.Name);?});
同樣打印出當遍歷Person集合時的結果一樣,看起來似乎很繁瑣,我們將代碼通過進行精簡如下:
????????????var?list?=?GetList();List<Animal>?animalList?=?list.Select((delegate(Person?p){????????????????return?new?Animal()?{?Name?=?p.Name,?Age?=?p.Age,?Gender?=?p.Gender?};})).ToList();animalList.ForEach(delegate(Animal?animal)?{?Console.WriteLine(animal.Name);?});
下面我們通過擴展方法來自定義實現Select()方法,代碼如下:
????????public?static?List<TR>?SelfDefineSelect<T,?TR>(this?List<T>?list,?Func<T,?TR>?fun)/*T為傳進來的泛型集合類型,TR為返回的泛型集合類型*/{List<TR>?selList?=?new?List<TR>();??/*實例化返回的泛型集合*/????????????foreach?(T?t?in?list){TR?tr?=?fun(t);??/*獲取傳進來的集合數據*/selList.Add(tr);}????????????return?selList;??/*返回新的泛型集合*/}
?依然是進行此調用,打印結果正確:
????????????List<Animal>?animalList?=?list.SelfDefineSelect((delegate(Person?p){????????????????return?new?Animal()?{?Name?=?p.Name,?Age?=?p.Age,?Gender?=?p.Gender?};})).ToList();
?(2)結合匿名類來實現
當我們使用Func根據條件轉換成新的集合時可能只需要幾個實例成員,這個時候如果還重新建立一個類來進行獲取就顯得有點小題大做了,這個時候只需將其需要的或的成員傳給一個匿名類即可,這個就是Func需要用到匿名類的場景。所以鑒于此,我們將返回的新的集合為匿名集合而非Animal集合,代碼改造如下:
????????????var?anyousList?=?list.Select((delegate(Person?p){????????????????return?new?{?Name?=?p.Name};?/*結合匿名類使用*/}));
通過上述所講系統內置泛型委托的實現,似乎有點不太令人滿意,關于委托的代碼太過繁瑣,是的微軟大大也明確知道了這點,于是乎,一步一步走向了高級,那就下文的lambda表達式,這結構的簡單簡直了。。。讓你爽到暴。
lambda表達式
?上述代碼說過可以精簡,如何精簡呢?那就是lambda表達式,匿名方法已經夠簡潔的了,但是lambda表達式是比匿名方法更加簡潔的一種語法!我們用lambda表達式來分別實現上述中的Action、Predicate以及Func委托。
Action
????????????var?list?=?GetList();list.ForEach(d?=>?Console.WriteLine(d.Name);)
Predicate
????????????var?list?=?GetList();list.FindAll(d?=>?d.Age?>?13);
Func
????????????list.Select(d?=>??Person()?{?Name?=?d.Name,?Age?=?d.Age,?Gender?==>??{?Name?=?d.Name});
好了,一切都變得如此明朗。自從有了lambda表達式,敲代碼的速度加快了,媽媽再也不用擔心我熬夜到很晚了。?
好了,問題來了,我們知道lambda表達式分為?語句lambda和表達式lambda??,那么二者有何區別呢?從字面上理解語句lambda是不是就是用大括號括起來的呢?ok,給出代碼來理解吧。
(string?str)?=>?{?return?str.length;?}??/*語句lambda(有大括號和return)*/(string?str)?=>?str.length??/*表達式lambda(沒有大括號和return,只有一個式子)*/
那問題又來了,lambda表達式到底是什么呢?我們依然用反編譯來查看??list.ForEach(d => Console.WriteLine(d.Age));?對應的C#代碼如下:
?看ForEach()方法里面的參數意思大概是匿名方法委托,接著我們點擊進去看看,代碼如下:
我們接著點擊Action看看,如下:
一下就豁然開朗了,這不正說明?lambda表達式的實質就是匿名方法?嗎!所以現在想想,lambda表達式的本質是匿名方法,匿名方法的本質是通過委托實現的。應該就是這樣了。
lambda表達式進化過程?
我們就一個擴展方法的實例來演示lambda表達式演變的過程是多么的惟妙惟肖。
假設如下場景:在花千骨電視中找出白子畫出來,找對了你就贏了!我們獲得給出一個花千骨眾角色列表,再選出白子畫即可。
?????????????/*根據條件找出所需,返回true你就贏了,反之則輸*/static?bool?SelDefine_Extension_IEnumerable<T>(this?IEnumerable<T>?source,?Func<T,?bool>?func){?????????????????foreach?(var?item?in?source){????????????????????if?(func(item)){????????????????????????return?true;}}?????????????????return?false;}
下面給出集合列表:
????????????var?list?=?new?List<string>()?{?"花千骨",?"白子畫",?"東方彧卿",?"霓漫天",?"糖寶",?"落十一",?"輕水",?"孟玄朗"?};
然后在控制臺執行擴展方法進行查詢,在此列出?lambda表達式6部曲?:
????????????list.SelDefine_Extension_IEnumerable(
從開始的繁瑣,復雜到最終的簡潔,每一個過程微軟大大也是作出一定的努力,先點給贊先!就上述用一副圖來看,估計會更加清晰明了吧
lambda表達式演變六部曲
轉載于:https://blog.51cto.com/hzz333/1918812