马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
总目次
前言
在C#编程中,Lambda表达式是一种简洁而强大的语法特性,它提供了一种更加灵活和直观的方式来编写匿名函数。无论是在LINQ查询、事件处理还是异步编程中,Lambda表达式都扮演偏重要脚色。本文将详细介绍Lambda,帮助您更好地明白和把握这一重要的C#特性。
一、Lambda 表达式 是什么?
1. 界说
Lambda表达式是C# 3.0引入的一个新特性,它是匿名方法的简化情势,允许开辟者以更简洁的方式界说函数。Lambda表达式可以用于任何必要委托的地方,而且可以在运行时动态创建函数对象。它的主要优点包罗代码简洁、易于阅读和维护。
2. 根本语法
Lambda表达式的语法非常简朴,由参数列表、=>符号和表达式或语句块组成。其根本情势如下:
- (parameters) => expression
复制代码 或
- (parameters) => { statements }
复制代码
- 参数列表:可以包含零个或多个参数,用逗号分隔。
- =>符号:称为“goes to”操纵符,表示从参数到表达式或语句块的映射。
- 表达式或语句块:可以是一个简朴的表达式或一个包含多条语句的代码块。
二、怎样使用 Lambda 表达式?
根据是否包含语句块,Lambda表达式可以分为两种范例:表达式Lambda 和 语句Lambda。
1. 使用 表达式Lambda
表达式Lambda是最简朴的情势,仅包含一个表达式,表达式Lambda 侧重于表达式的返回值。其语法如下:
- (parameters) => expression
- //如: x => x * 4
复制代码 1)演化过程
- public int Square(int num)
- {
- return num * num;
- }
复制代码 上面界说一个简朴的计算方法,现在我们必要以 Lambda 表达式表现出来,该怎样表现呢?
- Func<int, int> func = x => x * x;
复制代码 上述两个示例代码是等效的, x => x * x; 本质上等同于将一个有返回值的方法赋予了Func委托;
2)示例
- // 定义一个委托类型
- Func<int, int> square = x => x * x;
- // 调用委托
- Console.WriteLine(square(5)); // 输出: 25
复制代码 在这个例子中,我们界说了一个名为 square 的委托,它继承一个整数参数并返回该整数的平方值。通过Lambda表达式,我们可以非常简洁地实现这一功能。
2. 语句Lambda
语句Lambda允许包含多条语句,适用于必要执行复杂逻辑的情况。其语法如下:
- (parameters) => { statements }
复制代码 语句Lambda侧重语句块中执行内容,具体示例如下:
- // 定义一个委托类型
- Action<int> printAndSquare = x =>
- {
- Console.WriteLine($"Input: {x}");
- Console.WriteLine($"Square: {x * x}");
- };
- // 调用委托
- printAndSquare(5); // 输出: Input: 5 Square: 25
复制代码 在这个例子中,我们界说了一个名为 printAndSquare 的委托,它继承一个整数参数并打印输入值及其平方值。通过语句Lambda,我们可以轻松实现这一复杂的逻辑。
- Func<int, int> factorial = n =>
- {
- if (n == 0)
- return 1;
- else
- return n * factorial(n - 1);
- };
复制代码 3. Lambda 表达式的各种使用方式
1)无参数 与 空括号
将 lambda 表达式的输入参数括在括号中。 使用空括号指定零个输入参数。假如是没有参数,必须有这个空括号
- Action line = () => Console.WriteLine();
复制代码 2)方法体只有一行代码
假如方法体中只有一行代码,可以省略方法体大括号
- Action line1 = () => { Console.WriteLine("test"); };
- //省略方法体大括号
- Action line2 = () => Console.WriteLine("test");
复制代码 3)范例推断
C# 编译器会根据上下文自动推断参数范例,无需显式声明:
Lambda表达式的范例是由编译器根据上下文推断出来的,通常对应于某个委托范例(如 Func<T, TResult> 或 Action<T>)。假如Lambda表达式没有返回值,则其范例为 Action;假如有返回值,则其范例为 Func。
- Func<int, int, int> func3 = (int num1, int num2) => num1 * num2;
- //类型推断 允许我们省略 数据类型的显示声明
- Func<int, int, int> func4 = (num1, num2) => num1 * num2;
复制代码 4)无法范例推断时
一般情况下,编译器可自行推断输入参数的范例,但存在编译器无法推断的情况,则需自行指定
- Func<int, string, bool> isTooLong = (int x, string s) => s.Length > x;
复制代码 5)省略return
假如方法体中只有一行代码,且有返回值,可以省略return
- 当方法体中只有一行代码,但并没有返回值的时候,此时的Lambda表达式 是 【语句 Lambda】
- 当方法体中只有一行代码,且有返回值的时候,此时的Lambda表达式 是 【表达式 Lambda】
- // 语句 Lambda ,侧重于执行内容
- Action action = () => Console.WriteLine("ss");
- // 表达式 Lambda,侧重于返回值
- // 特征:一行代码 + 有返回值
- Func<int, int> func1 = x => x * x * x;
- Func<int, string> func2 = x => x.ToString();
复制代码 6)一个参数
假如只有一个输入参数,则括号无关紧要
- Func<int, int> func = (x) => x * x * x;
- //省略括号
- Func<double, double> cube = x => x * x * x;
复制代码 7) 2个参数以上
两个或更多输入参数使用逗号加以分隔
- Func<int, int, bool> testForEquality = (x, y) => x == y;
- Func<int, int, int> add = (x, y) => x + y;
复制代码 8)多行代码 + 显示 return
当方法体中具有多行代码,则 该Lambda表达式 是【语句Lambda】,假如必要返回数据,则必要显示使用return。
- Func<int, int, int> func3 = (num1, num2) =>
- {
- Console.WriteLine("计算:");
- return num1* num2;
- };
复制代码 三、Lambda 表达式的常见应用场景
1. 应用场景
Lambda表达式广泛应用于各种场景,以下是几个常见的应用场景:
- LINQ查询:在LINQ查询中,Lambda表达式常用于指定过滤条件、投影、排序等操纵。
- 事件处理:可以用Lambda表达式来简化事件处理步伐的编写。
- 异步编程:在异步编程中,Lambda表达式可用于界说异步任务的执行逻辑。
- 委托与回调:Lambda表达式可以作为委托的实现,用于回调函数或其他必要传递函数指针的场所。
2. 应用场景示例
1)Lambda与委托
- 内置委托范例 :Lambda表达式可隐式转换为Func或Action委托:
- // Func委托(带返回值)
- Func<int, int, int> add = (a, b) => a + b;
- // Action委托(无返回值)
- Action<string> log = msg => Console.WriteLine(msg);
复制代码 Lambda 表达式可以转换的委托范例由其参数和返回值的范例界说。 假如 Lambda 表达式不返回值,则可以将其转换为 Action 委托范例之一;否则,可将其转换为 Func 委托范例之一。
- 自界说委托 :Lambda可直接赋值给自界说委托范例:
- delegate int Operation(int a, int b);
- Operation add = (x, y) => x + y;
- Console.WriteLine(add(3, 4)); // 输出 7
复制代码 2)Lambda 在LINQ中的应用
Lambda是LINQ查询的基石,是编写查询条件的标准方式,用于界说筛选、排序和转换逻辑:
- var numbers = new List<int> { 1, 2, 3, 4, 5 };
- // 筛选大于3的元素
- var filtered = numbers.Where(n => n > 3); // 筛选 >3 的数
- // 转换为平方值
- var squares = numbers.Select(n => n * n); // 转换为平方值
复制代码- public class Program
- {
- public static void Main()
- {
- var numbers = new List<int> { 1, 2, 3, 4, 5 };
- // 筛选
- var evenNumbers = numbers.Where(x => x % 2 == 0);
- Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));
- // 排序
- var sortedNumbers = numbers.OrderBy(x => x);
- Console.WriteLine("Sorted numbers: " + string.Join(", ", sortedNumbers));
- // 投影
- var squaredNumbers = numbers.Select(x => x * x);
- Console.WriteLine("Squared numbers: " + string.Join(", ", squaredNumbers));
- }
- }
复制代码
- 表达式树:Lambda还可生成表达式树,供LINQ to SQL等框架剖析为SQL语句。
3) Lambda 在事件处理中的应用
Lambda 可简化事件处理步伐的编写:
- button.Click += (sender, e) => Console.WriteLine("按钮被点击!");
复制代码 4)Lambda 在异步编程中的应用
在异步编程中,Lambda表达式可以用于界说异步任务的执行逻辑。
- class Program
- {
- static async Task Main()
- {
- await Task.Run(() =>
- {
- Console.WriteLine("Task started.");
- for (int i = 0; i < 5; i++)
- {
- Console.WriteLine($"Processing item {i + 1}");
- }
- Console.WriteLine("Task completed.");
- });
- Console.WriteLine("Main method completed.");
- }
- }
复制代码 在这个例子中,我们使用Lambda表达式 () => { ... } 来界说异步任务的执行逻辑。
三、高级特性
1. 闭包(Closure)
Lambda 表达式可以捕捉上下文中(或 外部作用域 )的变量,形成闭包。
Lambda表达式可以捕捉其界说范围内的局部变量,这意味着Lambda表达式可以访问外部作用域中的变量。这种特性使得Lambda表达式在某些情况下非常强大和灵活。
- public class Program
- {
- public static void Main()
- {
- int factor = 2;
- Func<int, int> multiply = x => x * factor;
- Console.WriteLine(multiply(5)); // 输出: 10
- factor = 5;
- Console.WriteLine(multiply(5)); // 输出: 25(闭包保留了 factor 的引用)
- }
- }
复制代码 在这个例子中,Lambda表达式捕捉了外部变量 factor ,并在计算效果时使用了这个变量的值。
必要注意的是,被捕捉的变量在Lambda表达式被调用时仍然有效,因此要确保这些变量在其生命周期内不会被销毁或修改。
注意事项:
- 捕捉的变量生命周期与委托绑定,可能导致内存泄漏;
- 避免修改外部变量,防止逻辑杂乱。
- 闭包可能导致不测的变量共享,需谨慎处理。
2. 表达式树(Expression Tree)
Lambda 表达式可以转换为表达式树,用于表示代码的布局化情势。表达式树常用于动态生成代码或进行反射操纵。
- public class Program
- {
- public static void Main()
- {
- Expression<Func<int, int>> expression = x => x + 1;
- Console.WriteLine(expression.ToString()); // 输出: x => (x + 1)
- }
- }
复制代码 3. 异步 Lambda
Lambda 表达式可以用于异步操纵,通过 async 和 await 关键字实现。
- Func<Task<int>> asyncLambda = async () => await Task.Delay(1000).ContinueWith(_ => 42);
复制代码- using System;
- using System.Threading.Tasks;
- public class Program
- {
- public static void Main()
- {
- Func<int, Task<int>> asyncLambda = async x =>
- {
- await Task.Delay(1000);
- return x * x;
- };
- asyncLambda(5).ContinueWith(t => Console.WriteLine(t.Result)); // 输出: 25
- }
- }
复制代码- Func<Task> asyncTask = async () =>
- {
- await Task.Delay(1000);
- Console.WriteLine("异步完成");
- };
复制代码 4. 动态方法
Lambda 表达式可以用于动态生成方法,这在反射或动态调用中非常有用。
- using System;
- using System.Reflection.Emit;
- public class Program
- {
- public static void Main()
- {
- // 动态生成一个方法
- DynamicMethod dynamicMethod = new DynamicMethod("Add", typeof(int), new[] { typeof(int), typeof(int) }, typeof(Program).Module);
- ILGenerator il = dynamicMethod.GetILGenerator();
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldarg_1);
- il.Emit(OpCodes.Add);
- il.Emit(OpCodes.Ret);
- // 调用动态生成的方法
- Func<int, int, int> add = (Func<int, int, int>)dynamicMethod.CreateDelegate(typeof(Func<int, int, int>));
- Console.WriteLine(add(2, 3)); // 输出: 5
- }
- }
复制代码 5. 泛型 Lambda
Lambda 表达式可以用于泛型委托,进步代码的复用性增强灵活性。
- Func<T, string> toString = (T obj) => obj?.ToString();
复制代码 四、注意事项与限定
- 作用域规则:
- Lambda 无法捕捉 ref/out 参数。
- Lambda 内的return不影响外部方法。
- 外部方法无法访问 Lambda 内部界说的变量。
- 运算符限定:
- 语句限定:
- 不允许包含 goto、break、continue 凌驾 Lambda 边界的语句。
- 性能考量:
- 表达式树可能比直接委托生成更多内存开销。
- 频繁调用的Lambda需避免闭包捕捉,淘汰GC压力。
- 太过使用Lambda表达式可能影响性能,尤其是在循环中频繁创建委托时。
- Lambda 表达式在运行时会被编译为委托或表达式树,可能会有一定的性能开销。在性能敏感的场景中,必要谨慎使用。
- 循环变量捕捉:
- Lambda表达式捕捉循环变量时,可能会导致不测行为(多线程异步情况下)。应使用局部变量来避免。
- for (int i = 0; i < 3; i++)
- {
- int local = i;
- actions.Add(() => Console.WriteLine(local));
- }
复制代码
五、演化过程
- public delegate void ShowDelegate(int a,string b);
- public class LambdaEvolution
- {
- public void Show(int a1,string b1)
- {
- Console.WriteLine($"show{a1}:{b1}");
- }
- public void Test()
- {
- //1 .Netframework1.0/1.1,原始方法
- ShowDelegate showDelegate = new ShowDelegate(Show);
- //2 .NetFramework2.0,匿名方法,增加delegate,去掉单独定义方法
- showDelegate = delegate (int x,string y) { Console.WriteLine($"show{x}:{y}"); };
- //3 .NetFramework3.0,=> 引入Lambda表达式,去掉delegate
- showDelegate = (int s,string t) => { Console.WriteLine($"show{s}:{t}"); };
- //4 .NetFramework3.0后期,简化参数类型,编译器自动推断数据类型
- showDelegate = (s,t) => { Console.WriteLine($"show{s}:{t}"); };
- //5 如果方法体中只有一行代码,可以省略方法体大括号
- showDelegate = (s,t) => Console.WriteLine($"show{s}:{t}");
- //如果方法只有一个参数,可以省略参数小括号
- Action<string> action = x => Console.WriteLine($"show{x}");
- //如果方法体中只有一行代码,且有返回值,可以省略return
- Func<int, string> func = x => x.ToString();
- }
- }
复制代码 结语
回到目次页:C#/.NET 知识汇总
希望以上内容可以帮助到各人,如文中有不对之处,还请批评指正。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |