Lambda表达式可以采用以下任意一种形式的表达式:
1.表达式Lambda,表达式为其主体:
(input-parameters) => expression
2.语句Lambda,语句块作为其主体:
(input-parameters) => {<sequence-of-statements>}
使用Lambda声明运算符=>
,从其主体中分离Lambda参数列表。若要创建Lambda表达式,需要在Lambda运算符左侧指定输入参数(如果有参数时),然后在另一侧输入表达式或语句块。
任何Lambda表达式都可以转换为委托类型,可以转换的委托类型由参数和返回值的类型定义。如果Lambda表达式不返回值,则可以将其转换为Action
委托类型之一;否则,可将其转换为Func委托类型之一。例如,有两个参数且不返回值的Lambda表达式可转化为Action<T1, T2>
委托。有一个参数且不返回值的Lambda表达式可转换为Func<T, TResult>委托。
例如:Lambda表达式x => x * x,指定名为x的参数,并返回x的平方值,并将表达式x => x * x分配给委托类型的变量。
Func<int, int> Square = x => x * x; Console.WriteLine($"输出:{Square(10)}");
//输出:100
表达式Lambda还可以转换为表达式树类型,如下示例所示:
System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x; Console.
WriteLine($"输出:{e}"); //输出:x => (x * x)
可在需要委托类型或表达式树的实列的任何代码中使用Lambda表达式。用C#编写LINQ时,还可以使用Lambda表达式,如下所示:
int[] numbers = { 1, 5, 3, 9, 28, 35, 5, 32, 34 }; var squaredNumbers = numbers
.Select(x => x * x); Console.WriteLine(string.Join("", squaredNumbers));
表达式Lambda

表达式位于=>运算符右侧的Lambda表达式称为“表达式Lambda”。表达式Lambda广泛用于表达式树的构造,表达式Lambda会返回表达式的结果,并采用以下基本形式:
(input-parameters) => experssion
仅当Lambda只有一个输入参数时,括号才是可选择的,否则括号是必须的。
使用空括号指定零个输入参数:
Action action = () => Console.WriteLine();
括号内的两个或更多的输入参数使用逗号加以分离:
Func<int, int, bool> test = (x, y) => x == y;
有时编译器无法推断输入类型,可以显示指定类型:
Func<int, string, bool> testTo = (int x, string str) => str.Length > x;
输入参数必须全部为显示或者全部为隐式;否则,编译器会产生报错。
表达式Lambda的主体可以包含方法调用。不过,若要创建在.NET公共语言运行时的上下文之外(如SQL
Server中)计算的表达式树,不能在Lambda表达式中使用方法调用。因为在.NET公共语言运行时的上下文之外,方法将没有任何意义。

语句Lambda
语句Lambda与表达式Lambda表达类似,只是语句括在大括号中:
(input-parameters) => {}
语句Lambda的主体可以包含任意数量的语句;但是,通常不会多余两个或三个。
Action action = name =>
{
string greeting = $“你好,{name}”;
Console.WriteLine(greeting);
};
action(“莫扎特”);
语句Lambda也不能用于创建表达式目录树。

异步Lambda
通过使用async和await关键字,可以轻松创建包含异步处理的Lambda表达式和语句。
例如,通过Windows窗体示例包含一个调用和等待异步方法ExampleMethodAsync的事件处理程序。
public partial class Form1 : Form { public Form1() { InitializeComponent();
button1.Click += button1_Click; } private async void button1_Click(object sender
, EventArgs e) { await ExampleMethodAsync(); textBox1.Text += "\r\nControl
returned to Click event handler.\n"; } private async Task ExampleMethodAsync() {
// The following line simulates a task-returning asynchronous process. await
Task.Delay(1000); } }
可以使用异步Lambda添加同一事件处理程序。若要添加此程序,请在Lambda参数列表前添加async修饰符,如下面示例所示:
public partial class Form1 : Form { public Form1() { InitializeComponent();
button1.Click += async (sender, e) => { await ExampleMethodAsync(); textBox1.
Text+= "\r\nControl returned to Click event handler.\n"; }; } private async Task
ExampleMethodAsync() { // The following line simulates a task-returning
asynchronous process. await Task.Delay(1000); } }
Lambda表达式和元组

从C#7.0起,C#语言提供对元组的内置支持。可以提供一个元组作为Lambda表达式的参数,同时Lambda表达式也可以返回一个元组。在某些情况下,C#编译器使用类型推理来确定元组组件的类型。

可通过用括号括住用逗号分割的组件列表来定义元组。下面的示例使用包含三个组件的元组,将一系列数字传递给Lambda表达式,此表达式将每个值翻倍,然后返回包含乘法运算结果的元组(内含三个元组)。
Func<(int, int, int), (int, int, int)> Items = s => (2 * s.Item1, 2 * s.Item2,
2 * s.Item3); var nums = (3, 7, 4); var numbersItem = Items(nums); Console.
WriteLine($"The set {nums} doubled:{numbersItem}");
通常,元组字段命名为Item1、Item2等。但是,可以使用命名组件定义元组,如以下示例所示:
Func<(int n1, int n2, int n3), (int, int, int)> items = s => (2 * s.n1, 2 * s.
n2, 2 * s.n3); var num = (45, 34, 29); var itemNums = items(num); Console.
WriteLine($"The set {num} doubled:{itemNums}");
Lambda表达式中的类型推理

编写Lambda时,通常不必为输入参数指定类型,因为编译器可以根据Lambda主体、参数类型以及C#语言规范中描述的其它因素来推断类型。对于大多数标准查询运算符,第一个输入是源序列中的元素类型。如要查询
IEnumerable<Customer>,则输入变量将被推断为Customer对象,这就意味着你可以访问其属性和方法。
customer.Where(c => c.City == "London");
Lambda类型推理的一般规则如下:

* Lambda包含的参数数量必须与委托类型包含的参数数量相同; Lambda
* 中的每个输入参数必须都能够隐式转换为其对应的委托参数;
* Lambda的返回值(如果有)必须能够隐式转换为委托的返回类型。
注意:
Lambda表达式本身没有类型,因为通用类型系统没有Lambda表达式这一固有概念。不过,有时以一种非正式的方式谈论Lambda表达式的“类型”会很方便,在这些情况下,类型是指委托类型或Lambda表达式所转换到的Expression类型。

捕获Lambda表达式中的外部变量和变量范围

Lambda可以引用外部变量。这些变量是在定义Lambda表达式的方法中或包含Lambda表达式的类型中的范围内变量。以这种方式捕获的变量将进行存储以备在Lambda表达式中使用,即使在其它情况下,这些变量将超出范围并进行垃圾回收。必须明确地分配外部变量,然后才能在Lambda表达式中使用该变量。
如下示例所示:
public class Class1 { public class VariableCaptureGame { internal Action<int>
updateCapturedLocalVariable; internal Func<int, bool> isEqual; public void
RunCaptureGame(int data) { int k = 0; updateCapturedLocalVariable = x => { k = x
; bool result = k > data; Console.WriteLine($"{k} is greater than {data}:{result
}"); }; isEqual = x => x == k; Console.WriteLine($"Local variable before lambda
invocation:{k}"); updateCapturedLocalVariable(20); Console.WriteLine($"Local
variable after lambda invocation:{k}"); } } public void InvokeMain() { var game
= new VariableCaptureGame(); int gameInput = 8; game.RunCaptureGame(gameInput);
int jTry = 10; bool result = game.isEqual(jTry); Console.WriteLine($"Captured
local variable is equal to{jTry}:{result}"); int anotherData = 6; game.
updateCapturedLocalVariable(anotherData); bool anotherResult = game.isEqual(
anotherData); Console.WriteLine($"Another lambda observes a new value of
captured variable:{anotherResult}"); } }
下列规则适用于Lambda表达式中的变量范围:
捕获的变量将不会作为垃圾回收,直至引用变量的委托符合垃圾回收的条件;
在封闭方法中看不到Lambda表达式内引入的变量;
Lambda表达式无法从封闭方法中直接捕获in、ref或out参数;
Lambda表达式中的return语句不会导致封闭方法返回;

如果相应跳转语句的目标位于Lambda表达式块之外,Lambda表达式不得包含goto、break或continue语句。同样,如果目标在块内部,在Lambda表达式块外部使用跳转语句也是错误的。

技术
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:766591547
关注微信