如果你是资深.NET开发者,肯定熟悉基础操作:.Where、.Select,甚至可能用过.GroupBy。但LINQ的深度远不止于此。
本文不讨论简单内容——我们将深入探索那些不常见但你应该掌握的实用模式。
🧰 1. 自定义查询操作符——一次编写,随处使用 你是否在各个项目中重复某些操作?没错,你可以为这些操作构建自己的LINQ风格扩展方法。
🧪 批处理操作符 需要处理海量数据集但又想控制负载?这个正适合你。
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
using var e = source.GetEnumerator();
while (e.MoveNext())
yield return GetBatch(e, size);
}
private static IEnumerable<T> GetBatch<T>(IEnumerator<T> e, int size)
{
do yield return e.Current;
while (--size > 0 && e.MoveNext());
}
使用场景:分批处理10000条记录,每批1000条,避免内存爆炸。
foreach (var batch in dataset.Batch(1000))
await ProcessBatchAsync(batch);
这在实际应用中出人意料地少见,但对于限流处理、分页和外部API批处理极其有用。
🧠 2. 使用规约模式进行查询组合 当你动态构建过滤器时,代码很容易变成一堆条件判断的烂摊子。以下方法可以避免这种情况。
public abstract class Specification<T>
{
public abstract Expression<Func<T, bool>> ToExpression();
public static implicit operator Expression<Func<T, bool>>(Specification<T> spec) => spec.ToExpression();
}
创建具体的过滤器:
public class ActiveUsers : Specification<User>
{
public override Expression<Func<User, bool>> ToExpression() => u => u.IsActive;
}
然后像搭乐高一样组合它们:
var query = users.Where(new ActiveUsers().And(new HighPriority()));
这种方法可测试、可重用,当你的LINQ代码开始变得像意大利面条时,这将是必备利器。
🚀 3. 延迟求值优化——停止为不需要的东西买单 你知道这个技巧很好——但有多少人能始终如一地应用它?
// ❌ 急切求值 = 浪费资源
var result = data.Select(Expensive).Where(x => x.IsValid).Take(5).ToList();
// ✅ 延迟求值 = 明智之举
var result = data
.Where(CheapPreFilter)
.Select(Expensive)
.Where(x => x.IsValid)
.Take(5)
.ToList();
为什么这很重要:
LINQ默认采用延迟求值。好好利用这一特性。
🧊 4. 被遗忘的英雄:窗口函数(又称移动聚合) 需要对序列进行滑动窗口操作(例如计算移动平均),但LINQ没有直接提供相应功能?
public static IEnumerable<IEnumerable<T>> Window<T>(this IEnumerable<T> source, int size)
{
var buffer = new Queue<T>(size);
foreach (var item in source)
{
buffer.Enqueue(item);
if (buffer.Count == size)
{
yield return buffer.ToArray();
buffer.Dequeue();
}
}
}
使用场景:
var movingAverages = temperatures.Window(3).Select(w => w.Average());
如果你从未用过这个功能,欢迎进入时间序列LINQ的全新世界。
🧩 5. 扁平化层级结构——无需递归 处理嵌套树形结构?这是最简洁的解决方案。
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> childSelector)
{
foreach (var item in source)
{
yield return item;
foreach (var child in childSelector(item).Flatten(childSelector))
yield return child;
}
}
使用场景:扁平化组织架构、文件夹结构或组件树:
var allEmployees = departments.SelectMany(d => d.Employees.Flatten(e => e.DirectReports));
这种方法虽然不常见,但非常优雅,在处理复杂的递归模型时极具价值。
🧪 6. 自定义聚合——超越.Sum()和.Average() 需要计算众数(Mode)?或者标准差(StandardDeviation)?
public static T Mode<T>(this IEnumerable<T> source)
{
return source
.GroupBy(x => x)
.OrderByDescending(g => g.Count())
.First().Key;
}
public static double StandardDeviation(this IEnumerable<double> values)
{
var avg = values.Average();
return Math.Sqrt(values.Sum(v => Math.Pow(v - avg, 2)) / values.Count());
}
使用场景:
var mostCommonStatus = orders.Select(o => o.Status).Mode();
var priceStdDev = products.Select(p => p.Price).StandardDeviation();
LINQ是可扩展的——不要局限于内置功能。
🔁 7. 函数式模式:LINQ与单子(Monad)的结合 让我们玩点高级的。你可以构建支持LINQ的单子链,用于建模失败、部分结果或异步工作流。
public static IEnumerable<TResult> Bind<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TResult>> selector)
{
return source.SelectMany(selector);
}
var results = data
.Bind(Validate)
.Bind(Transform)
.Bind(Save);
这些不是你日常使用的.Where().Select().ToList()模式。这些是锋利的工具——当性能至关重要、当可读性很重要、当代码需要超越"仅仅能用"而具备扩展性时,你就会拿出这些工具。
当你将LINQ的声明式能力与高级模式结合时,你不仅仅是在编写代码——你是在架构逻辑流。