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
;