C# lambda表达式

lambda表达式

C# 中的 lambda 表达式是一种简洁的表示匿名函数的方法。Lambda 表达式特别用于 LINQ 查询、事件处理程序、回调函数等场景中,它使得代码更加简洁和易于阅读。

Lambda 表达式的基本语法如下:

csharp 复制代码
(input-parameters) => expression-or-statement-block

这里 input-parameterslambda 表达式的参数列表,它可以包含零个、一个或多个参数。=>lambda 运算符,它分隔参数列表和表达式或语句块。expression-or-statement-blocklambda 主体,可以是单个表达式或语句块。

下面是一些 lambda 表达式的例子:

无参数 lambda 表达式:

csharp 复制代码
() => Console.WriteLine("Hello, World!")

单个参数 lambda 表达式:

csharp 复制代码
x => x * x // 这是一个简单的平方计算 lambda

多个参数 lambda 表达式:

csharp 复制代码
(x, y) => x + y // 这是一个简单的加法计算 lambda

带有语句块的 lambda 表达式:

csharp 复制代码
(x, y) => {  
    int sum = x + y;  
    return sum * sum; // 返回两数之和的平方  
}

在实际使用中,lambda 表达式经常与委托或 FuncAction泛型委托结合使用。例如:

csharp 复制代码
Func<int, int, int> add = (x, y) => x + y;  
int result = add(5, 3); // result 将会是 8

在这个例子中,我们定义了一个名为 addFunc<int, int, int> 类型的委托,它接受两个 int 类型的参数并返回一个 int 类型的值。Lambda 表达式 (x, y) => x + y 被赋值给这个委托,然后我们可以像调用普通方法一样调用这个委托。

Lambda 表达式在 LINQ 查询中也非常有用,例如:

csharp 复制代码
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };  
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
// 筛选偶数

在这个 LINQ 查询中,n => n % 2 == 0 是一个 lambda 表达式,它定义了筛选偶数的条件。

关于参数

C#中,当使用lambda表达式时,参数的类型确实可以省略,这是因为在大多数情况下,编译器可以根据lambda表达式使用的上下文(比如它赋值给的委托类型或者它所在的方法签名)来推断出参数的类型。这称为类型推断

例如,如果你有一个接受Func<int, int, bool>委托的方法,你可以这样传递一个lambda表达式,而不需要显式指定参数类型:

csharp 复制代码
Func<int, int, bool> isDivisible = (a, b) => a % b == 0;

在这个例子中,编译器可以推断出ab都是int类型,因为Func<int, int, bool>委托期望两个int类型的参数和一个bool类型的返回值。

然而,在某些情况下,你可能需要显式指定参数类型,特别是当lambda表达式没有直接赋值给委托或者当上下文不足以让编译器进行类型推断时。在这些情况下,你可以像下面这样显式指定参数类型:

csharp 复制代码
(int a, int b) => a % b == 0

显式指定类型可以提供更好的代码可读性,特别是在复杂的lambda表达式或者当参数类型不是显而易见的时候。不过,在大多数简单场景中,省略类型并使用类型推断可以使代码更简洁。

总的来说 ,是否需要在lambda表达式中指定参数类型取决于具体的使用场景和上下文。在能够利用类型推断的情况下,通常可以省略类型以简化代码。

表达式或语句块

C#中,lambda表达式的返回行为取决于它的使用上下文。如果lambda表达式被赋值给一个返回类型的委托(比如Func系列委托),或者它用于表达式树中且需要返回一个值,那么lambda表达式必须包含返回语句(return),除非它的主体本身就是一个表达式(在这种情况下,表达式的值就是lambda的返回值)。

如果lambda表达式赋值给的是一个没有返回值的委托(比如Action系列委托),或者它是事件处理器等不需要返回值的场景,那么它不应该包含return语句。在这些情况下,lambda表达式的主体通常是一个语句块,执行一系列操作但不返回任何值。

以下是一些例子来说明这些概念:

有返回值的lambda表达式(使用return):

csharp 复制代码
Func<int, int, bool> isDivisible = (a, b) => {  
    return a % b == 0; // 使用return语句返回bool值  
};

或者,如果lambda的主体只是一个表达式,你可以省略大括号和return语句:

csharp 复制代码
Func<int, int, bool> isDivisible = (a, b) => a % b == 0; 
// 表达式的结果就是返回值

没有返回值的lambda表达式(不使用return):

csharp 复制代码
Action<string> printMessage = message => {  
    Console.WriteLine(message); // 执行操作,不返回任何值  
};

在这个例子中,printMessage是一个Action<string>类型的委托,它接受一个字符串参数并执行一个操作(打印消息到控制台),但不返回任何值。因此,lambda表达式的主体是一个语句块,它包含了一系列操作语句,而不是一个返回语句。

总结来说 ,是否需要在lambda表达式中使用return语句取决于该lambda表达式所代表的函数的预期行为:是否需要返回一个值。如果需要返回值,则必须使用return语句(除非lambda的主体本身就是一个表达式);如果不需要返回值,则不应该使用return语句。

一个神奇的例子与总结

csharp 复制代码
class TODO {
    public static void Main(String[] args) {
        Func<int, int ,double >rule = (x, y) => {
            return x + y;
        };

        Console.WriteLine(rule(1, 2));

    }
}

/*
var rule = new Func<int, int, double>((x, y) => {
    return x + y;
});
*/

C#中,你提供的两个代码片段定义了一个接受两个int类型参数并返回一个double类型结果的Func委托。虽然这两个代码片段在功能上完全相同,但它们在语法和可读性方面有一些细微的差别。

第一个代码片段

csharp 复制代码
Func<int, int, double> rule = (x, y) => {  
    return x + y;  
};

在这个片段中,rule是一个Func<int, int, double>类型的变量,它直接通过lambda表达式进行初始化。这种语法在C#中非常常见,因为它简洁且直接。这里,lambda表达式(x, y) => { return x + y; }定义了委托的行为。

第二个代码片段

csharp 复制代码
var rule = new Func<int, int, double>((x, y) => {  
    return x + y;  
});

注意 其中的varFunc<int, int, double>

在这个片段中,rule同样是一个Func<int, int, double>类型的变量,但它是通过new关键字和Func的构造函数来初始化的。虽然这种语法是完全有效的,但它通常不如第一个片段那样常见或推荐,因为它稍微冗长一些。不过,这种语法在某些情况下可能更清晰,尤其是当需要在初始化委托的同时进行其他操作时。

区别总结
语法简洁性 :第一个代码片段更简洁,因为它直接通过lambda表达式进行初始化,而不需要使用new关键字和构造函数。

可读性 :对于大多数C#开发者来说,第一个代码片段可能更易于阅读和理解,因为它直接反映了委托的类型和它的行为。

性能 :在实际运行时,这两种语法在性能上是没有区别的。它们都会创建一个新的委托实例,并将lambda表达式编译成的方法作为该委托的调用目标。

灵活性:虽然第一个代码片段更常见,但第二个代码片段在某些特定场景下可能更有用,尤其是当需要更复杂的初始化逻辑时。

在实际编程中,建议使用第一个代码片段的语法,因为它更简洁、更常见,也更容易被其他开发者理解。

换句话说

C#中,当你使用lambda表达式来创建委托的实例时,你实际上是在隐式地创建一个匿名函数 并将其赋值给委托变量 。在这个过程中,你不需要显式地使用new关键字来创建委托的实例,因为编译器会自动为你处理这些细节。

例如,下面的代码展示了如何使用lambda表达式来隐式地创建一个Func<int, int, bool>类型的委托实例:

csharp 复制代码
Func<int, int, bool> isDivisible = (x, y) => x % y == 0;

在这个例子中,我们没有使用new关键字来创建Func<int, int, bool>的实例。相反,我们直接将lambda表达式(x, y) => x % y == 0赋值给了isDivisible变量。编译器会自动处理lambda表达式的编译和委托实例的创建。

然而,如果你想要显式地通过委托的构造函数来创建一个委托实例 ,那么你需要使用new关键字,就像这样:

csharp 复制代码
Func<int, int, bool> isDivisible = new Func<int, int, bool>((x, y) => x % y == 0);

虽然这种语法是有效的,但它通常是不必要的 ,并且会使代码看起来更加冗长。直接使用lambda表达式来赋值给委托变量是更常见和推荐的做法。

总之,当你使用lambda表达式来创建委托实例时,你不需要显式地使用new关键字。编译器会自动为你处理这些细节,使代码更加简洁和易读。

相关推荐
dhxhsgrx1 小时前
PYTHON训练营DAY25
java·开发语言·python
敲代码的 蜡笔小新4 小时前
【行为型之中介者模式】游戏开发实战——Unity复杂系统协调与通信架构的核心秘诀
unity·设计模式·c#·中介者模式
风逸hhh4 小时前
python打卡day25@浙大疏锦行
开发语言·python
刚入门的大一新生4 小时前
C++初阶-string类的模拟实现与改进
开发语言·c++
程序猿多布4 小时前
使用Visual Studio将C#程序发布为.exe文件
c#·visual studio
chxii5 小时前
5java集合框架
java·开发语言
老衲有点帅5 小时前
C#多线程Thread
开发语言·c#
C++ 老炮儿的技术栈5 小时前
什么是函数重载?为什么 C 不支持函数重载,而 C++能支持函数重载?
c语言·开发语言·c++·qt·算法
IsPrisoner6 小时前
Go语言安装proto并且使用gRPC服务(2025最新WINDOWS系统)
开发语言·后端·golang