前面的文章介紹過,C# 1.0中出現委託這個核心概念,在C# 2.0中,委託獲得了很大的改進。C# 2.0中委託的改進爲C# 3.0中的新特性提供了鋪墊,當咱們瞭解了匿名方法後,Lambda的學習就會變得相對容易。ide
下面就看看C# 2.0中委託的改進。函數
在C# 1.0中,若是要建立一個委託實例,就必須同時指定委託類型和符合委託簽名的方法。可是,在C# 2.0中,支持了方法組轉換,也就是說咱們能夠從方法組到一個兼容委託類型的隱式轉換。所謂"方法組"(method group),其實就是一個方法名。學習
看一個例子:this
class Program { public delegate void ReverseStringHandler(string str); private static void ReverseString(string str) { char[] charArray = str.ToCharArray(); Array.Reverse(charArray); Console.WriteLine(charArray); } static void Main(string[] args) { //C# 1.0中建立委託實例 ReverseStringHandler reverseStringHandler = new ReverseStringHandler(ReverseString); reverseStringHandler("Hello World"); //C# 2.0中經過方法組轉換建立委託實例 reverseStringHandler = ReverseString; reverseStringHandler("Good morning"); } }
經過方法組轉換,繁瑣的委託實例建立獲得了簡化。spa
根據C# 1.0中瞭解到的知識,當咱們建立一個委託實例的時候,咱們須要找到一個跟委託類型簽名一致的方法,用這個方法來實例化一個委託對象。線程
看看前面的字符串反轉的例子,可能"ReverseString"這個方法在程序中只會使用一次,可是爲了建立委託實例,這個方法必需要存在。設計
在C# 2.0中引入了匿名方法,因此有了更簡單的方式實現上面的例子:3d
public delegate void ReverseStringHandler(string str); static void Main(string[] args) { ReverseStringHandler reverseStringHandler = delegate(string str) { char[] charArray = str.ToCharArray(); Array.Reverse(charArray); Console.WriteLine(charArray); }; reverseStringHandler("Hello World"); Console.Read(); }
從上面能夠看到,匿名方法就是經過delegate關鍵字以及參數列表和具體語句塊的實現。code
因此說,若是用來實例化委託的方法比較簡單,而且這個方法在其餘地方使用的頻率很低時,這時候就能夠考慮用匿名方法來進行簡化。對象
若是你對匿名方法仍有疑惑,建議你看看上面例子的IL代碼。
在Main函數的IL代碼中,能夠看到編譯器爲咱們生成了一個名爲"<Main>b__0"的方法,方法接受一個string類型的參數。
.method private hidebysig static void Main ( string[] args ) cil managed { …… IL_0009: ldftn void AnonymousMethod.Program::'<Main>b__0'(string) …… } // end of method Program::Main
當咱們查看"<Main>b__0"方法的IL代碼後,能夠看到這個方法就是咱們在匿名方法的語句塊中定義的操做。
.method private hidebysig static void '<Main>b__0' ( string str ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2050 // Code size 23 (0x17) .maxstack 1 .locals init ( [0] char[] charArray ) IL_0000: nop IL_0001: ldarg.0 IL_0002: callvirt instance char[] [mscorlib]System.String::ToCharArray() IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call void [mscorlib]System.Array::Reverse(class [mscorlib]System.Array) IL_000e: nop IL_000f: ldloc.0 IL_0010: call void [mscorlib]System.Console::WriteLine(char[]) IL_0015: nop IL_0016: ret } // end of method Program::'<Main>b__0'
匿名方法的實現原理就是:編譯器將在匿名方法所在的類,爲每一個匿名方法都建立了一個方法。編譯器建立的這些方法只在IL代碼中有效,在C#代碼中是無效的,因此C#代碼不能直接使用這些方法。
其實,匿名方法更經常使用的地方是把匿名方法看成一個參數傳遞給另外一個方法。你們確定都知道List有一個FindAll的方法來查找符合條件的item,這裏FindAll的參數就是一個過濾條件的委託。
List<int> numList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; foreach(int num in numList.FindAll(delegate(int n){return n>6;})) { Console.WriteLine(num); }
說到了這裏,咱們就看看系統幫咱們定義的委託,在C#中,Action、Func和Predicate是系統定義的委託,咱們能夠直接使用。上面的FindAll的參數就是一個Predicate<T>的泛型委託。
Action<T>是無返回值的泛型委託,Action委託能夠支持至少0個參數,至多16個參數。
例如:
對於前面的字符串反轉的例子,咱們可使用Action委託進一步簡化,這樣咱們連"ReverseStringHandler"這個委託也省略了:
static void Main(string[] args) { Action<string> reverseString = delegate(string str) { char[] charArray = str.ToCharArray(); Array.Reverse(charArray); Console.WriteLine(charArray); }; reverseString("Hello World"); Console.Read(); }
前面看到Action委託是沒有返回值的,爲了解決咱們有時可能須要返回值的問題,系統中又出現了Func委託。
Func<TResult>是有返回值的泛型委託,其中TResult就表明返回值的類型()。Func委託能夠支持至少0個參數,至多16個參數(Func<T1,T2,…,T16,TResult>)。
例如:
看個簡單的例子:
Func<int, int, int> addOperation = delegate(int numA, int numB) { return numA + numB; }; Console.WriteLine(addOperation(3,4));
predicate 是返回bool型的泛型委託,經常結合集合類的查詢使用;Predicate有且只有一個參數,返回值固定爲bool。
Predicate原型:public delegate bool Predicate<T> (T obj)
在前面結合查詢的例子中,咱們直接把匿名方法"delegate(int n){return n>6;}"傳給了FindAll方法。
其實也能夠寫成,
Predicate<int> checkNum = delegate(int n) { return n > 6; }; foreach (int num in numList.FindAll(checkNum)) {……}
在有些狀況下,咱們並不須要委託參數,那麼匿名方法能夠進一步省略參數列表,只須要使用一個delegate關鍵字,加上做爲方法的操做使用的代碼塊。
看一個簡單的代碼段:
Action printer = delegate { Console.WriteLine("Hello world"); }; printer();
注意,這個"參數通配"(paremeter wildcarding)的特性並不能適用全部的狀況,若是匿名方法可以轉換成多個委託類型,那麼咱們就須要給編譯器提供更多的信息。
舉個例子,線程的構造函數設計兩個委託類型,一個有參數,一個無參數。
public delegate void ParameterizedThreadStart(object obj)
public delegate void ThreadStart()
因此,當咱們經過下面的語句建立線程的時候,前兩條語句沒有問題,可是第三條語句會有一個錯誤。
由於第三條語句中的匿名方法能夠轉換成多個委託類型,編譯器就不知道怎麼處理了,因此,咱們須要顯示給出參數列表。
Thread t1 = new Thread(delegate() { Console.WriteLine("this is t1"); }); Thread t2 = new Thread(delegate(object o) { Console.WriteLine("this is t2"); }); Thread t3 = new Thread(delegate { Console.WriteLine("this is t3"); });
本篇文章介紹了C# 2.0中委託的改進,經過方法組轉換和匿名方法,能夠簡化程序。
同時,看到了系統定義的三個委託類型,因此有些時候咱們能夠不用建立本身的委託;可是要這個權衡,若是咱們要常常使用一個特定類型的委託,那仍是建議定義一個有意義更加明顯的委託類型。
下一篇咱們將一塊兒看看匿名方法中的變量。