lambda表達式不簡化寫起來和匿名函數很像,而匿名函數通常賦值給委托,通過委托進行調用。以下我們對lambda和委托的基本規則與使用進行整理,同時為了加深理解和記憶,我們整理了委托是如何一步步演化到lambda。
1. 委托
委托是一個可以指向方法的類型,調用委托變量時,執行的就是變量指向的方法,以下介紹的是委托的基本用法。
1.1 自定義委托
委托的定義與類不同,更像是C中的函數指針聲明。定義一個委托要注意以下幾點:
- 最重要的是要做到類型兼容:
- 參數列表(參數個數,參數數據類型)一致
- 返回值數據類型一致
- 注意定義委托的位置,因為委托和類是平級關系,所以通常把委托聲明在名稱空間體里面;
namespace ConsoleApp3
{//定義委托delegate double Calc(double x, double y);internal class Program{//定義方法public static double Add(double x, double y){ return x + y;}static void Main(string[] args){//使用委托Calc calc = Add;Console.WriteLine(calc(1.2, 8.8));Console.Read();}}
}
使用委托時還應注意:
- 上面例子中:
Calc calc = Add;
,這里要注意傳入方法時不能帶括號,帶括號表示調用 - 既然委托的本質也是類,同樣可以new一個實例:
Calc calc = new Calc(Add);
1.2 泛型委托
自定義泛型委托
<>
內是類型參數列表,用于替換使用的返回值以及參數列表中的參數類型;
namespace ConsoleApp3
{delegate T AddDele<T>(T a, T b);internal class Program{static void Main(string[] args){AddDele<double> addDele = Add;Console.WriteLine(addDele(100, 200));}public static double Add(double x, double y){return x + y;}}
}
Action
.NET中已經為我們定義好了無返回值的泛型委托Action,泛型參數列表與傳入方法的參數列表相對應。如果沒有泛型參數列表,表示該方法無參數列表,無返回值。
static void Main(string[] args)
{Action<string> action = ShowString;action("Hello");Console.Read();
}public static void ShowString(string str)
{Console.WriteLine(str);
}
Func
.NET中已經為我們定義好了有返回值的泛型委托Func,泛型參數列表的最后一個參數代表返回值類型,最后一個之前的參數與傳入方法的參數列表相對應;如果參數列表只有一個類型,表示該方法沒有參數只有一個返回值;
static void Main(string[] args)
{Func<double, double, double> func = Add;Console.WriteLine(func(1.2, 8.8));Console.Read();
}public static double Add(double x, double y)
{return x + y;
}
1.3 委托指向匿名函數
匿名函數顧名思義就是沒有名字的函數,該函數可以綁定到委托類型的變量上。匿名函數的定義形式為:delegate + (參數列表) + {方法體}
。
static void Main(string[] args)
{Action action = delegate (){Console.WriteLine("I am anonymous function");};action();Func<int, int, int> func = delegate (int x, int y) {return x + y; };Console.WriteLine(func(1, 2));
}
1.4 委托轉換
不同的委托不能混用,這很好理解,不同的Class即使里面的屬性,方法,字段等全都一樣,也是兩個不同的類型,委托也一樣,即使定義了兩個委托參數列表和返回值類型一樣,仍然是不同的委托,不可以混用;舉個例子:
namespace ConsoleApp3
{delegate int AddDele1(int x, int y);delegate int AddDele2(int x, int y);internal class Program{static void Main(string[] args){AddDele1 addDele1 = Add;AddDele2 addDele2 = addDele1;Console.WriteLine(addDele1(1, 2));Console.WriteLine(addDele2(2, 3));Console.Read();}public static int Add(int x, int y){return x + y;}}
}
結果編譯器報錯了:Cannot implicitly convert type 'ConsoleApp3.AddDele1' to 'ConsoleApp3.AddDele2''
,看來不同的委托無法進行隱式轉換,正確的做法應該是:
namespace ConsoleApp3
{delegate int AddDele1(int x, int y);delegate int AddDele2(int x, int y);internal class Program{static void Main(string[] args){AddDele1 addDele1 = Add;AddDele2 addDele2 = new AddDele2(addDele1);Console.WriteLine(addDele1(1, 2));Console.WriteLine(addDele2(2, 3));Console.Read();}public static int Add(int x, int y){return x + y;}}
}
2. 從委托到lambda的演化
我們從一個正常的委托調用,看看是如何一步步進化到一個最簡lambda表達式的。
1)委托指向普通方法:
private static void CommonDele()
{Action<string> action = ShowStr;action("CommonDele");Func<int, int> func = ShowNumber;Console.WriteLine(func(1));
}private static void ShowStr(string str)
{Console.WriteLine(str);
}private static int ShowNumber(int a)
{return a;
}
2)委托指向匿名方法:delegate + 參數列表 + 方法體
private static void AnonymousDele()
{Action<string> action = delegate (string str){Console.WriteLine(str);};action("AnonymousDele");Func<int, int> func = delegate (int a){return a;};Console.WriteLine(func(2));
}
3)引入lambda表達式:參數列表 + => + 方法體
private static void LambdaDele()
{Action<string> action = (string str) =>{Console.WriteLine(str);};action("LambdaDele");Func<int, int> func = (int a) =>{return a;};Console.WriteLine(func(3));
}
4)簡化lambda表達式:可以省略參數類型,因為編譯器能夠根據委托類型推斷出參數的類型
private static void LambdaDele2()
{Action<string> action = (str) =>{Console.WriteLine(str);};action("LambdaDele2");Func<int, int> func = (a) =>{return a;};Console.WriteLine(func(4));
}
5)簡化lambda表達式:如果只有一個參數,參數的()
也可以省略
private static void LambdaDele3()
{Action<string> action = str =>{Console.WriteLine(str);};action("LambdaDele3");Func<int, int> func = a =>{return a;};Console.WriteLine(func(5));
}
6)簡化lambda表達式:如果方法體沒有返回值,且方法體只有一行代碼,可省略 {}
;如果有返回值,且方法體中只有一行代碼,那么可以省略方法體的{}
以及return
private static void LambdaDele4()
{Action<string> action = str => Console.WriteLine(str);action("LambdaDele4");Func<int, int> func = a => a;Console.WriteLine(func(6));
}
3. lambda
根據上面從委托到lambda的演化過程,我們整理下lambda的基本規則:
- 基本形式:
參數列表 + => + 方法體
- 可以省略參數數據類型,因為編譯器可根據委托類型推斷參數類型
- 如果只有一個參數,參數的
()
也可以省略,多于一個不可以省略 - 如果方法體沒有返回值,且方法體只有一行代碼,可省略
{}
- 如果方法體有返回值,且方法體只有一行return語句,那么可以省略方法體的
{}
以及return
;