在 C# 中,函数式编程中常见的 map、filter、reduce 操作可以通过 LINQ(Language Integrated Query) 方便地实现。它们分别对应以下方法:
| 函数式操作 | C# LINQ 方法 | 说明 |
|---|---|---|
map |
Select |
将序列中的每个元素映射为新形式 |
filter |
Where |
根据条件筛选元素 |
reduce |
Aggregate |
对序列进行累积计算(累加、累乘等) |
下面给出一个完整的示例,演示如何对一个整数列表依次进行筛选(filter)、映射(map)和归约(reduce)。
csharp
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
// filter: 保留偶数
var filtered = numbers.Where(n => n % 2 == 0);
// map: 每个数平方
var mapped = filtered.Select(n => n * n);
// reduce: 求和
int result = mapped.Aggregate((acc, n) => acc + n);
// 可以链式调用,一气呵成
int result2 = numbers
.Where(n => n % 2 == 0) // filter
.Select(n => n * n) // map
.Aggregate((acc, n) => acc + n); // reduce
Console.WriteLine($"结果: {result}"); // 输出 56 (4+16+36)
Console.WriteLine($"链式结果: {result2}"); // 同样输出 56
}
}
常用变体与说明
Select可以投射出新的类型,例如.Select(x => new { Name = x })。Where可以串联使用,进一步细化筛选条件。Aggregate可以指定种子值,例如Aggregate(0, (acc, n) => acc + n),等价于Sum();但Aggregate更通用,可以实现累乘、拼接字符串等。- 如果只是简单的求和、求平均、求最大值等,可以使用专用方法
Sum()、Average()、Max()、Min()等,它们都是归约操作的简化版。
查询表达式语法(可选)
除了方法语法,C# 还提供了查询表达式语法,更接近 SQL:
csharp
var result = (from n in numbers
where n % 2 == 0
select n * n).Sum();
这种方法在复杂查询时可读性更好,但归约操作通常仍需使用方法语法(如 Sum())。
总结
在 C# 中,利用 LINQ 可以轻松实现函数式编程中的核心操作,且代码简洁、类型安全。以上示例展示了 map、filter、reduce 的对应实现,实际开发中可根据需求组合使用。
当然可以。不使用 LINQ,我们完全可以通过普通循环 和委托 手动实现类似 map、filter、reduce 的功能。下面提供两种常见的实现方式:直接使用循环 ,或自定义扩展方法 (无需 System.Linq 命名空间)。
1. 直接使用循环
最原始的方式就是显式编写 foreach 或 for 循环,依次进行筛选、转换和累积。
csharp
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
// filter: 保留偶数
List<int> filtered = new List<int>();
foreach (var n in numbers)
{
if (n % 2 == 0)
filtered.Add(n);
}
// map: 每个数平方
List<int> mapped = new List<int>();
foreach (var n in filtered)
{
mapped.Add(n * n);
}
// reduce: 求和
int result = 0;
foreach (var n in mapped)
{
result += n;
}
Console.WriteLine(result); // 56
}
}
这种方式直观,但代码冗长,且每一步都要创建中间集合,不适合大型数据流。
2. 自定义扩展方法(模拟 LINQ 风格)
我们可以自己实现类似 Select、Where、Aggregate 的扩展方法,完全脱离 System.Linq。这些方法内部仍然使用循环,但提供了链式调用的优雅语法。
csharp
using System;
using System.Collections.Generic;
public static class MyLinqExtensions
{
// 模拟 Select (map)
public static IEnumerable<TResult> MySelect<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
foreach (var item in source)
{
yield return selector(item);
}
}
// 模拟 Where (filter)
public static IEnumerable<TSource> MyWhere<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
foreach (var item in source)
{
if (predicate(item))
yield return item;
}
}
// 模拟 Aggregate (reduce)
public static TAccumulate MyAggregate<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func)
{
TAccumulate result = seed;
foreach (var item in source)
{
result = func(result, item);
}
return result;
}
}
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
int result = numbers
.MyWhere(n => n % 2 == 0) // filter
.MySelect(n => n * n) // map
.MyAggregate(0, (acc, n) => acc + n); // reduce
Console.WriteLine(result); // 56
}
}
关键点:
- 使用
yield return实现延迟执行(流式处理),避免中间集合。 Func委托让调用者自定义逻辑。- 这些方法本质上就是 LINQ 内部实现方式的简化版,但完全不需要引用
System.Linq命名空间。
3. 使用普通静态方法(不依赖扩展方法)
如果不喜欢扩展方法,也可以直接写静态方法,手动传入集合和委托:
csharp
public static class MyFunctions
{
public static IEnumerable<T> Filter<T>(IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (var item in source)
if (predicate(item))
yield return item;
}
public static IEnumerable<TResult> Map<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> mapper)
{
foreach (var item in source)
yield return mapper(item);
}
public static TAccumulate Reduce<TSource, TAccumulate>(IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> reducer)
{
TAccumulate result = seed;
foreach (var item in source)
result = reducer(result, item);
return result;
}
}
// 使用
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var filtered = MyFunctions.Filter(numbers, n => n % 2 == 0);
var mapped = MyFunctions.Map(filtered, n => n * n);
int sum = MyFunctions.Reduce(mapped, 0, (acc, n) => acc + n);
Console.WriteLine(sum); // 56
总结
- 不使用 LINQ 时,可以通过循环 + 委托完全实现
map、filter、reduce。 - 自定义扩展方法可以保留链式调用的优雅性,且不依赖
System.Linq。 - 手动实现的好处是更清晰地理解内部机制,且在某些受限环境(如不允许引用 LINQ 的程序集)中仍然可用。
实际开发中,如果项目允许,推荐直接使用 LINQ,因为它更简洁、可读性更高且经过了高度优化。但在需要精确控制或学习原理时,自己实现也是很好的练习。