超越.Where和.Select:7个高级LINQ模式,让.NET开发效能飙升

作者:微信公众号:【架构师老卢】
9-23 14:1
180

如果你是资深.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的声明式能力与高级模式结合时,你不仅仅是在编写代码——你是在架构逻辑流。

相关留言评论
昵称:
邮箱:
阅读排行