C# 中的函数式编程:一种现代化的开发方法
函数式编程(FP)是一种将计算视为数学函数求值,并避免状态变化和可变数据的编程范式。尽管 C# 最初被设计为面向对象的语言,但从 3.0 版本开始,它已积极地融入了函数式方法的元素。如今,C# 中的函数式编程不仅仅是一种时髦的潮流,更是编写更可预测、更可测试且更安全并发代码的强大工具。
在本文中,我们将详细解析应用于 C# 的函数式编程核心概念:纯函数、数据不可变性、LINQ、闭包以及 Lambda 表达式。你将看到这些技巧如何帮助我们在 .NET 生态系统中编写简洁且可靠的代码。
1. 纯函数与无副作用
纯函数是一种满足以下条件的函数:
- 对于相同的参数,始终返回相同的结果(引用透明性)。
- 没有副作用(不修改全局变量,不执行输入/输出操作,不改变传入的参数)。
在 C# 中,纯函数可能看起来像这样:
// 纯函数public int AddPure(int a, int b){ return a + b;}
// 非纯函数(副作用 —— 控制台输出和修改全局变量)private int _state = 0;public int AddImpure(int a, int b){ _state++; Console.WriteLine("函数调用"); return a + b + _state;}纯函数的优势显而易见:它们易于测试、可预测,并且可以安全地从并行线程中调用。尝试将所有与 I/O 无关的逻辑提取到纯函数中。
2. 数据不可变性
函数式编程鼓励使用不可变数据。与其修改现有对象,不如创建一个带有已更改属性的新对象。在 C# 中,以下方式非常适合实现这一点:
- record(从 C# 9 开始)—— 一种引用类型,默认具有基于值的相等性和不可变性。
- struct —— 用于小型不可变结构(例如,坐标、货币金额)。
- IEnumerable<T> —— 通过 LINQ 实现的不可变集合。
使用 record 的示例:
public record Person(string Name, int Age);
// 使用 with 表达式创建包含修改的副本var original = new Person("Alexey", 30);var updated = original with { Age = 31 };
Console.WriteLine(original); // Person { Name = Alexey, Age = 30 }Console.WriteLine(updated); // Person { Name = Alexey, Age = 31 }这种方法防止了在一个地方意外修改数据而影响程序其他部分的情况。
3. LINQ —— 函数式数据查询
LINQ(语言集成查询)或许是在 C# 中体现函数式方法最突出的例子。它允许使用方法链以声明式风格处理集合。
关键的函数式 LINQ 方法:
Select—— 转换每个元素(类似于 map)。Where—— 过滤(类似于 filter)。Aggregate—— 折叠(类似于 reduce)。SelectMany—— 展平嵌套集合(flatMap)。
示例:求偶数的平方和。
int[] numbers = { 1, 2, 3, 4, 5, 6 };
// 命令式风格int sumImperative = 0;foreach (var num in numbers){ if (num % 2 == 0) { sumImperative += num * num; }}
// 使用 LINQ 的函数式风格int sumFunctional = numbers .Where(n => n % 2 == 0) // 过滤 .Select(n => n * n) // 转换 .Sum(); // 聚合