02011402 委托02-匿名方法、Lambda表达式
02011402 委托02-匿名方法、Lambda表达式
1. 匿名方法
- 至此,我们已经介绍了使用静态方法或实例方法来实例化委托。在这种情况下,方法本身都可以被代码的其它部分显式调用。
- 如果方法只会使用一次,在这种情况下,除了创建委托的语法需要,没有必要创建独立的具名方法。
- 匿名方法是在实例化委托时内联声明的方法。
1.1 比较具名方法和匿名方法
// @1使用具名方法来实例化委托
using System;
namespace Demo01
{
delegate int MyDel(int x);
class MyClass
{
public static int Add1(int x)
{
return x += 2;
}
static void Main()
{
MyDel mDel1 = Add1; // 通过具名方法来实例化委托
Console.WriteLine($"Value:{mDel1(10)}");
Console.ReadLine();
}
}
}
控制台输出:
Value:12
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// @2 使用匿名方法来实例化委托
using System;
namespace Demo01
{
delegate int MyDel(int x);
class MyClass
{
static void Main()
{
MyDel mDel1 = delegate (int x) // 通过匿名方法来实例化委托
{
return x += 2;
};
Console.WriteLine($"Value:{mDel1(10)}");
Console.ReadLine();
}
}
}
控制台输出:
Value:12
1.2 使用匿名方法
- 可以在如下地方使用匿名方法。
- 声明委托变量时作为初始化表达式。
- 组合委托时在赋值语句的右边。
- 为委托增加事件时在赋值语句的右边。(后续章节会讲解)
1.3 匿名方法的语法
delegate(Parameter){ImplementationCode} // 匿名方法表达式
↑ ↑ ↑
关键字 参数列表 语句块
说明:
1. 匿名方法表达式要有delegate类型关键字。
2. 匿名方法表达式要有参数列表,如果语句块没有任何参数则可以省略。
3. 语句块,它包含了匿名方法的代码。
1.4 匿名方法的返回类型
- 匿名方法不会显式声明返回值。然而,实现代码本身的行为必须通过返回一个与委托的返回值类型相同的值来匹配委托的返回值。
delegate int MyDel(int myInt)
static void Main()
{
MyDel mDel = delegate (int x) // 匿名方法不会显式的声明返回值
{
retuan x + 20; // 返回一个整型值
}
}
说明:如上代码中,委托的返回类型是int,因此匿名方法的实现代码也必须在代码路径中返回int。
1.5 匿名方法的参数
- 除了参数数组,匿名方法的参数列表必须在如下3方面与委托匹配。
- 参数数量。
- 参数类型及其位置。
- 修饰符。
- 可以通过()为空或省略圆括号来简化匿名方法的参数列表,但必须满足如下两个条件。
- 委托的参数列表不包括任何out参数。
- 匿名方法不适用任何参数。
delegate void MyDel (int x); // 委托参数列表没有out参数
MyDel mDel = delegate // 没有参数列表
{
PrintMessage(); // 方法体中没有使用任何参数
}
1.6 匿名方法省略params参数
- 如果委托声明的参数列表包含了params参数,那么匿名方法的参数列表将忽略params关键字。
delegate void MyDel(int X, params int[] Y); // 在委托声明中使用params关键字
...
MyDel mDel = delegate (int x, int[] y) // 在匹配的匿名方法中省略关键字params
{
...
}
说明:
1. 委托类型声明指定最后一个参数为params类型的参数。
2. 匿名方法参数列表必须省略params关键字。
2. 匿名方法的变量和参数的作用域
- 参数以及声明在匿名方法内部的局部变量的作用域限制在实现代码的主体之内。
delegate void MyDel(int x);
MyDel mDel = delegate (int y)
{
int z = 10;
Console.WriteLine($"{y},{z}");
}
Console.WriteLine(($"{y},{z}"); // 编译错误
2.1 匿名方法捕获外部变量
- 与委托的具名方法不同,匿名方法可以访问它们外围作用域的局部变量和环境。
- 外围作用域的变量叫做外部变量。
- 用在匿名方法实现代码中的外部变量称为被方法捕获。
int x = 5;
MyDel mDel = delegate
{
Console.WriteLine($"{x}");
}
说明:匿名方法会“捕获”外部变量。
2.2 捕获变量的生命周期的拓展
- 只要捕获方法是委托的一部分,即使变量已经离开了作用域,捕获的外部变量也会一直有效。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo01
{
delegate void MyDel();
class Program
{
static void Main()
{
MyDel mDel;
// 在{}包含的这个块中定义了变量x,那么x在这个块中有效
{
int x = 5; // 变量x定义在匿名方法外面的块
mDel = delegate
{
Console.WriteLine($"{x}"); // 变量x被匿名方法捕获
};
}
// Console.WriteLine($"{x}"); // @1 超出了x的作用域,错误写法。
if (null != mDel)
mDel(); // 在块的外部,这里使用了匿名方法内部的x
Console.ReadLine();
}
}
}
控制台输出:
5
说明:
1. 局部变量x在块中声明和初始化。
2. 委托mDel用匿名方法初始化,该匿名方法捕获了外部变量x。
3. 块关闭时,x超出了作用域,因此@1处的写法错误。
4. mDel委托中的匿名方法在它的环境中保留了x,并在mDel调用时输出了它的值。
3. Lambda表达式
- 在C# 2.0中引入了匿名方法。在C# 3.0引入了Lambda表达式,简化了匿名方法的语法,从而避免包含多余的信息。
- 可以使用Lambda表达式来替代匿名方法,其实如果C#先引入了Lambda表达式,那么就没有匿名方法这个说法。
3.1 Lambda表达式的语法格式
// 匿名方法的格式
MyDel mDel = delegate (int x) {return x + 1;};
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 匿名方法转换成Lambda表达式
MyDel mDel = (int x) => {return x + 1;};
- 在匿名方法中delegate关键字有点多余,因为编译器已经知道我们将方法赋值给委托。我们可以很容易的通过如下步骤把匿名方法转换成Lambda表达式。
- 删除delegate关键字。
- 在参数列表和匿名方法主体之间放置Lambda运算符“=>”,该运算符读作goes to。
3.2 更加简洁的Lambda表达式
delegate MyDel(int); // 定义委托
...
MyDel del = delegate (int x) {return x + 1;}; // 匿名方法
MyDel lamDel01 = (int x) => {return x + 1;}; // 显式类型Lambda表达式,此时Lambda表达式带有类型的参数列表。
MyDel lamDel02 = (x) => {return x + 1;}; // 隐式类型Lambda表达式,此时Lambda表达式省略类型的参数列表,类型可以推断出来。
MyDel lamDel03 = x => {return x + 1;}; // 只有一个类型参数,可以省略()。
MyDel lamDel04 = x => x + 1; // Lambda表达式允许表达式的主体是语句块或表达式。如果语句块只包含了一个返回语句,我们可以将语句块替换为return关键字后的表达式。
- 因为编译器可以从委托的声明中知道委托参数的类型,因此Lambda表达式允许省略类型参数。在上述代码段中采用Lambda表达式转换少了一些多余的东西,代码看上去更加简洁。
3.3 Lambda表达式参数列表
- 有关Lambda表达式参数列表的要点如下。
- Lambda表达式参数列表中的参数必须在参数数量,类型和位置上与委托匹配。
- Lambda表达式参数列表中的参数不一定需要包含类型(隐式类型),除非委托有ref和out参数,此时必须注明类型(显式类型)。
- 如果只有一个参数,并且是隐式类型的,则两端()可以省略,否则必须有括号。
- 如果没有参数,必须使用一组空的()。
结尾
书籍:C#图解教程
著:【美】丹尼尔 · 索利斯;卡尔 · 施罗坦博尔
译:窦衍森;姚琪琳
ISBN:978-7-115-51918-4
版次:第5版
发行:人民邮电出版社
※敬请购买正版书籍,侵删请联系85863947@qq.com※
※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※