本文主要是介绍一篇文章厘清C#中的lambda表达式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一篇文章厘清C#中的lambda表达式
链接: 源码
说C#的匿名函数,就要先说一下匿名函数.
Lambda表达式
- 1 lambda表达式演变史
- 1. **C# 1.0 (2002)**
- 2. **C# 2.0 (2005)**
- 3. **C# 3.0 (2007)**
- 4. **C# 4.0及以后**
- 2 lambda表达式使用方法
- 1 **基本语法**
- 2 **使用场景和示例**
- **作为参数**
- **匿名委托**
- **LINQ 查询表达式**
- **事件处理**
- **并行编程与任务调度**
- **延迟执行与 Lazy<T>**
- 3 **注意事项**
- 3 系统自带的两种委托:Action和Func
- **Action**
- **用法示例**
- **应用场景**
- **Func**
- **用法示例**
- **应用场景**
- 4 综合案列
1 lambda表达式演变史
C# 匿名函数的演变历史可以追溯到 C# 语言的不同版本,随着语言特性的不断丰富和发展,匿名函数经历了以下几个主要阶段:
1. C# 1.0 (2002)
在 C# 1.0 中,虽然还没有直接支持匿名函数的概念,但已经引入了委托(Delegate)这一关键概念。委托允许将方法作为参数传递或存储为变量,为后续匿名函数的引入奠定了基础。在这个版本中,若要创建委托实例,必须先定义一个具有匹配签名的方法,然后使用该方法的名称来初始化委托。
例如:
public delegate int MyDelegate(int x, int y);public static int AddNumbers(int a, int b)
{return a + b;
}MyDelegate add = new MyDelegate(AddNumbers);
2. C# 2.0 (2005)
C# 2.0 引入了匿名方法,这是对匿名函数功能的初步实现。匿名方法允许开发者在需要委托的地方直接编写一段代码块(内联),而无需事先定义一个命名方法。这种语法简化了在特定上下文中临时创建和使用简单功能的过程,特别是在事件处理和回调场景中。
匿名方法的语法如下:
MyDelegate add = delegate(int a, int b)
{return a + b;
};
3. C# 3.0 (2007)
C# 3.0 引入了更强大的匿名函数形式——Lambda 表达式。Lambda 表达式进一步简化了匿名方法的语法,使其更加简洁且易于阅读。Lambda 表达式可以直接表示输入参数、箭头符号(=>
)以及要执行的表达式或语句块。它们在LINQ(Language Integrated Query)中扮演了核心角色,极大地增强了C#的函数式编程能力。
Lambda 表达式的语法示例:
// 单行表达式形式
MyDelegate add = (int a, int b) => a + b;// 多行语句块形式
MyDelegate process = (int a, int b) =>
{int result = a * b;Console.WriteLine("Processing numbers...");return result;
};
4. C# 4.0及以后
随着Lambda表达式的普及和广泛使用,匿名方法在新项目中的使用逐渐减少,Lambda表达式成为编写匿名函数的首选方式。后续版本的C#(如4.0、5.0、6.0、7.0、8.0、9.0、10.0等)继续强化和扩展了Lambda表达式的能力,包括:
- 类型推断:Lambda表达式中的参数类型可以根据上下文自动推断,进一步减少了代码冗余。
- 可变数量参数:Lambda表达式支持可变数量参数,方便处理不定长度的参数列表。
- async/await支持:C# 支持异步Lambda表达式,便于编写非阻塞的异步代码。
- Expression-bodied members:C# 6.0引入了表达式体成员语法,使得Lambda风格的简短表达式可以用于方法、属性、构造函数等更多场景。
- Local functions(局部函数):虽然不是匿名函数,但C# 7.0引入的局部函数提供了另一种在方法内部定义私有、嵌套函数的方式,有时可以作为匿名函数的替代方案,尤其是在需要复用或避免闭包副作用的情况下。
综上所述,C# 匿名函数的演变历史始于C# 2.0的匿名方法,经由C# 3.0的Lambda表达式实现了重大飞跃,并在后续版本中持续得到增强和完善,成为现代C#编程中不可或缺的一部分。尽管匿名方法在早期版本中有其作用,但在当前实践中,Lambda表达式已成为编写匿名函数的标准方式。
2 lambda表达式使用方法
C# Lambda 表达式是一种简洁、灵活的匿名函数表示形式,广泛应用于各种编程场景,如 LINQ 查询、事件处理、委托实例化、高阶函数应用等。以下是使用 C# Lambda 表达式的一些基本方法和常见用例:
1 基本语法
(input-parameters) => { <sequence-of-statements> }
Lambda 表达式的语法由三部分组成:
-
参数列表:位于圆括号
()
内,可以为空(表示无参数)、包含一个或多个参数,参数类型可以显式声明或根据上下文推断。- 显式类型:
(int x, string y)
- 类型推断:
(x, y)
—— 当Lambda表达式赋值给已知委托类型或在编译器可以确定类型的情境下,可以省略参数类型。
- 显式类型:
-
箭头操作符
=>
:将参数列表与表达式或语句块分隔开。 -
表达式或语句块:表示Lambda表达式的行为。
-
单行表达式:直接返回表达式的计算结果,不需要使用
return
关键字。int[] numbers = { 1, 2, 3 }; var evenNumbers = numbers.Where(n => n % 2 == 0);
-
多行语句块:使用花括号
{}
包围,如果需要执行多条语句或需要显式return
语句,则使用语句块形式。Func<int, int> squareAndLog = number => {int squared = number * number;Console.WriteLine($"Squared: {squared}");return squared; };
-
2 使用场景和示例
作为参数
Lambda 表达式常被用作需要函数作为参数的方法或操作符的参数,如 LINQ 方法、事件处理器、Action
或 Func
委托实例化等。
// LINQ 查询中的 Where 方法
var filteredItems = items.Where(item => item.IsAvailable && item.Price > 100);// 使用 Action 委托
Action<string> logMessage = message => Console.WriteLine($"Logging: {message}");
logMessage("Hello, Lambda!");// 使用 Func 委托
Func<int, int, int> add = (a, b) => a + b;
int sum = add(3, 5);
匿名委托
Lambda 表达式可以替代传统的匿名方法,创建不需显式定义的、临时使用的委托实例。
// 传统匿名方法
button.Click += delegate (object sender, EventArgs e)
{MessageBox.Show("Button clicked!");
};// Lambda 表达式形式
button.Click += (sender, e) => MessageBox.Show("Button clicked!");
LINQ 查询表达式
Lambda 表达式是 LINQ 查询语法的核心组成部分,用于定义筛选、投影、聚合等操作。
var customers = context.Customers.Where(c => c.Country == "USA").Select(c => new { Name = c.Name, TotalPurchases = c.Purchases.Sum(p => p.Amount) }).OrderByDescending(x => x.TotalPurchases);
事件处理
Lambda 表达式简化了事件处理器的注册,尤其在需要访问外部变量时,可以利用闭包特性。
string greeting = "Welcome!";
button.Click += (sender, e) => MessageBox.Show(greeting);
并行编程与任务调度
Lambda 表达式在 Task.Run
、Parallel.ForEach
等并行编程场景中非常有用。
// 创建并启动一个后台任务
Task.Run(() =>
{DoSomeWork();UpdateUI();
});// 并行处理集合
Parallel.ForEach(numbers, number =>
{if (IsPrime(number))primes.Add(number);
});
延迟执行与 Lazy
Lambda 表达式用于初始化 Lazy<T>
对象,确保资源仅在首次访问时才进行计算或加载。
Lazy<int> expensiveComputation = new Lazy<int>(() => ComputeExpensiveValue());
int result = expensiveComputation.Value; // 计算仅在此处发生
3 注意事项
-
类型推断:Lambda 表达式的参数类型和返回类型通常可以由编译器推断,无需显式声明。但在某些情况下,可能需要显式提供类型信息以消除歧义。
-
闭包:Lambda 表达式可以捕获其封闭作用域内的变量,形成闭包。理解闭包行为对于避免潜在的并发问题和资源管理问题至关重要。
-
性能:Lambda 表达式通常编译为高效代码,但在某些情况下(如大型循环中的复杂Lambda表达式),可能会导致编译器生成额外的类和方法,影响性能。适当优化或使用局部函数替代可能有助于提升效率。
总之,C# Lambda 表达式提供了简洁、直观的方式来编写匿名函数,极大地提高了代码的可读性和可维护性,尤其在处理函数式编程、事件处理、委托、LINQ 查询等方面发挥着重要作用。理解和熟练运用Lambda表达式是现代C#开发中的重要技能。
3 系统自带的两种委托:Action和Func
C# 中的 Action
和 Func
是预定义的泛型委托类型,它们简化了委托的使用,避免了手动声明相似用途的自定义委托。今后我们使用时,没有必要自定义委托了,全部使用系统自带的委托就可以了,方便省事.
下面分别介绍 Action
和 Func
的用法:
Action
Action
代表一个无返回值的方法,只用于封装需要执行的操作。根据需要传递的参数数量,C# 提供了一系列预定义的 Action
类型,从 Action
(无参数)到 Action<typeparamref name="T1">, ..., T16</typeparamref></typeparamref></typeparamref>
(最多16个参数)。
用法示例
-
无参数:
Action noParamAction = () => Console.WriteLine("No parameter action called."); noParamAction(); // 输出 "No parameter action called."
-
带参数:
Action<string, int> paramAction = (message, count) => Console.WriteLine($"{message}, count: {count}"); paramAction("Action with parameters", 5); // 输出 "Action with parameters, count: 5"
应用场景
-
事件处理:作为事件处理器,执行某种操作,无需返回值。
button.Click += (sender, e) => Console.WriteLine("Button clicked!");
-
回调:传递给异步操作或其他方法作为完成时的回调函数。
Task.Run(() => LongRunningOperation()).ContinueWith(_ => Console.WriteLine("Long running operation completed."));
Func
Func
代表一个有返回值的方法,除了封装操作外,还返回一个指定类型的值。Func
类型同样有一系列预定义版本,格式为 Func<typeparamref name="T1">, ..., Tn</typeparamref>, TResult>
,其中 T1
到 Tn
代表输入参数类型,TResult
代表返回值类型。
用法示例
-
无参数,返回整数:
Func<int> noParamFunc = () => DateTime.Now.Second; int currentSecond = noParamFunc(); // 获取当前秒数
-
带参数,返回字符串:
Func<int, string, string> paramFunc = (num, text) => $"{text} {num}"; string combined = paramFunc(42, "The answer is"); // 结果为 "The answer is 42"
应用场景
-
LINQ 查询:作为查询表达式中的选择器(
Select
)、谓词(Where
)等方法的参数。List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; var squares = numbers.Select(n => n * n); // 返回平方数的集合
-
工厂方法:传递给需要动态创建对象的函数。
Func<string, MyClass> factory = name => new MyClass { Name = name }; MyClass instance = factory("Instance created by Func");
-
计算或转换:在需要简单计算或数据转换的场景中作为参数传递。
double average = CalculateAverage(scores, score => score / 10.0); // 转换分数为小数
总结来说,Action
用于封装无需返回值的操作,常用于事件处理、回调等场景;而 Func
用于封装带有返回值的方法,适用于需要计算、转换或作为函数参数返回结果的情况。两者均通过泛型参数来适应不同数量和类型的参数需求,大大简化了委托的使用。
4 综合案列
{ //.netframework 1.0MyDelgate myDelgate = new MyDelgate(MyFunc);myDelgate("keson");void MyFunc(string s){Console.WriteLine($"hello,{s}");}
}{ //.netframework 2.0MyDelgate myDelgate = new MyDelgate(delegate (string s){Console.WriteLine($"hello,{s}");});myDelgate("keson");
}{ //.netframework 3.0MyDelgate myDelgate = new MyDelgate((string s) =>{Console.WriteLine($"hello,{s}");});myDelgate("keson");
}{//省略参数关键字MyDelgate2 myDelgate = new MyDelgate2((s, n) =>{Console.WriteLine($"hello,{s},I am {n} years old");});myDelgate("keson", 18);
}{ //省略方法体的大括号MyDelgate2 myDelgate = new MyDelgate2((s, n) =>Console.WriteLine($"hello,{s},I am {n} years old"));myDelgate("keson", 18);
}{//匿名类object ImplicitClass = new{Id = 1,Name = "jack"};Console.WriteLine(ImplicitClass.GetType().Name);//C#是强类型语言,编译时会确定类型,ImplicitClass编译时是object类型,//object类本身没有Id属性,所以无法访问//Console.WriteLine(ImplicitClass.Id);//dynamic避开编译器检查dynamic ImplicitClass2 = new{Id = 99,Name = "keson"};Console.WriteLine(ImplicitClass2.Id);Console.WriteLine(ImplicitClass2.GetType().Name);// ImplicitClass2.瞎写();//编译时不检查,但是运行时会抛出异常.var ImplicitClass3 = new{Id = 89,Name = "keson",Age = 18};Console.WriteLine(ImplicitClass3.Id);Console.WriteLine(ImplicitClass3.GetType().Name);//ImplicitClass3.Id = 1; //匿名类是只读类型,只能初始化是设置
}{//使用系统自带的委托action和funcAction action = () => { Console.WriteLine("无参"); }; //无入参Action<string> action2 = s => { Console.WriteLine($"有参: {s}"); };//有入参Func<string> func = () => { return "keson"; };//无入参,有返回值Func<int,int> func2 = i => { return i * i; };//有入参,有返回值Func<int, int> func3 = i => i + i;//当方法体只有一行,关键字return可以省略,大括号可以省略Console.WriteLine(func3(3));
}public delegate void MyDelgate(string s);
public delegate void MyDelgate2(string s, int num);
这篇关于一篇文章厘清C#中的lambda表达式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!