C# 函数式编程:高级技术与模式

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C# 函数式编程:从语法到架构

\n\n

现代 C# 早已超越了纯面向对象语言的范畴。从 3.0 版本(LINQ)开始,一直到最新的 .NET 版本,该语言积极吸收函数式编程(FP)的范式。如果您已经掌握了 lambda 表达式和 LINQ,但希望更深入地探索,那么本文正适合您。我们将剖析高级 FP 概念,这些概念将使您的 C# 代码更可预测、更易测试且更简洁。

\n\n

1. 纯函数与副作用处理

\n\n

纯函数 是指满足以下条件的函数:

\n
    \n
  • 对于相同的参数,始终返回相同的结果(引用透明性)。
  • \n
  • 没有副作用(不修改全局状态,不写入文件,不发送 HTTP 请求)。
  • \n
\n\n

在 C# 中,纯函数的理想示例是数学运算。问题在于,实际应用程序不能仅由纯函数组成:迟早需要保存数据或发送邮件。解决方案是 将逻辑与副作用分离。

\n\n

高级技巧:使用 函子(Functor)和单子(Monad) 来封装副作用。考虑一个简单的 Maybe<T> 示例(类似于 nullable,但更具函数式风格):

\n\n
public class Maybe<T>\n{\n    private readonly T _value;\n    public bool HasValue { get; }\n    \n    private Maybe(T value, bool hasValue) => (_value, HasValue) = (value, hasValue);\n    \n    public static Maybe<T> Some(T value) => new Maybe<T>(value, true);\n    public static Maybe<T> None() => new Maybe<T>(default, false);\n    \n    // 函子:Select (Map)\n    public Maybe<TResult> Select<TResult>(Func<T, TResult> selector) =>\n        HasValue ? Maybe<TResult>.Some(selector(_value)) : Maybe<TResult>.None();\n    \n    // 单子:SelectMany (FlatMap/Bind)\n    public Maybe<TResult> SelectMany<TResult>(Func<T, Maybe<TResult>> binder) =>\n        HasValue ? binder(_value) : Maybe<TResult>.None();\n}\n\n// 使用示例\nMaybe<int> result = Maybe<int>.Some(10)\n    .Select(x => x * 2)\n    .SelectMany(x => x > 15 ? Maybe<int>.Some(x) : Maybe<int>.None());
\n\n

这段代码不会抛出异常,也无需手动检查 null —— 所有处理值缺失的逻辑都封装在单子中。

\n\n

2. 柯里化与部分应用

\n\n

在经典 FP 中,每个函数只接受一个参数。柯里化 是将一个接受多个参数的函数转换为一系列嵌套函数,每个函数只接受一个参数。部分应用 则是固定部分参数。

\n\n

C# 没有内置的柯里化,但可以通过闭包轻松实现:

\n\n
// 普通函数\nFunc<int, int, int, int> add = (a, b, c) => a + b + c;\n\n// 柯里化版本\nFunc<int, Func<int, Func<int, int>>> curriedAdd = a => b => c => a + b + c;\n\n// 部分应用:固定第一个参数\nFunc<int, Func<int, int>> add5 = curriedAdd(5);\n\n// 使用示例\nint sum = add5(10)(20); // 35
\n\n

高级场景:通过部分应用实现依赖注入。无需通过类构造函数传递配置或服务,可以在组合阶段将其固定:

\n\n
public class ReportService\n{\n    public Func<string, string, string> FormatReport => \n        (template, data) => $"[{DateTime.Now}] {template}: {data}";\n}\n\n// 使用部分应用\nvar service = new ReportService();\nvar formatWithTemplate = service.FormatReport("Error"); // 固定模板\nstring errorMessage = formatWithTemplate("Connection failed");                

博客

书籍推荐