LINQ 从 0 到 1:完整演变指南(核心术语解析 + 全注释实战)

0 阅读12分钟

目标读者:C# 初学者、想理解 LINQ 底层原理的开发者 学习成果:不仅能熟练使用 LINQ,更能手写实现 LINQ 核心方法 前置知识:基础 C# 语法、Lambda 表达式 可参考我上节 Lambda 教程:mp.weixin.qq.com/s/FdVIgwhLH…


第一部分:基础理论(先搞懂这三个核心概念)

在写代码之前,你必须先理解 LINQ 的三大基石。这些概念贯穿整个演变过程,搞懂了再看代码,会有一种"原来如此"的通透感。

1.1 什么是 LINQ?

LINQ(Language Integrated Query) = 语言集成查询

通俗说:LINQ 是 C# 提供的一种统一查询语法,让你用类似 SQL 的风格操作各种数据源(List、数组、数据库、XML 等)。

核心特点:

  • 声明式编程:告诉电脑"我要什么",而不是"怎么做"
  • 链式调用:可以 list.Where(...).Select(...).OrderBy(...) 连续操作
  • 类型安全:编译时检查类型,避免运行时错误
  • 延迟执行:不立即执行,遍历时才真正计算(节省内存)

1.2 核心术语解析(新手必懂)

术语通俗解释代码中的作用类比
predicate (谓词/条件)一个返回 bool 的方法/委托,比如 x => x > 10告诉程序"筛选什么条件的元素",是 Where 的核心输入像 SQL 的 WHERE 子句
selector (选择器一个转换方法/委托,比如 x => x * 2告诉程序"把元素转换成什么",是 Select 的核心输入像 SQL 的 SELECT
yield (迭代器关键字)读作"产出", yield return 表示"延迟返回一个元素"实现延迟执行:不用一次性加载所有数据,用一个取一个像流水线的"按需生产"
IEnumerable<T>可枚举接口,表示"可以被遍历的集合"LINQ 方法的返回类型,支持 foreach 遍历像"承诺可以遍历"的契约
扩展方法给现有类型"添加"新方法,不用修改原类list.Where(...) 这种调用成为可能像给手机装 App,不改动手机本身

1.3 yield 的深层理解(LINQ 高效的核心)

yield 是 C# 迭代器的灵魂,也是 LINQ 延迟执行的底层支撑。

return vs yield return 对比

// 传统 return:一次性计算所有结果,占用内存
static List<int> GetNumbersReturn()
{
    var result = new List<int>();
    for (int i = 1; i <= 1000000; i++)
    {
        result.Add(i);  // 全部加载到内存
    }
    return result;      // 一次性返回
}

// yield return:按需返回,遍历一个产出一个,几乎不占内存
static IEnumerable<int> GetNumbersYield()
{
    for (int i = 1; i <= 1000000; i++)
    {
        yield return i;  // 返回一个,暂停,下次从这里继续
    }
}

执行流程图解:

传统 return 模式:
[计算 1-100万][全部存入 List][返回整个 List][使用者遍历]
     ↑ 耗时久      ↑ 内存占用大              ↑ 延迟高

yield return 模式:
[准备迭代][返回 1][暂停][使用者要 2][返回 2][暂停] → ...
   ↑ 立即返回   ↑ 按需生产    ↑ 零延迟      ↑ 用一个取一个  ↑ 省内存

关键结论:LINQ 的 WhereSelect 等方法内部都用 yield return,所以能处理百万级数据而不爆内存。 实例:

using System.Diagnostics;

namespace _0001_yield_的深层理解
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 输出程序标题
            Console.WriteLine("===== return vs yield return 内存对比演示 =====");
            Console.WriteLine($"当前进程初始内存占用: {GetCurrentMemoryMB():F2} MB\n");

            // 测试传统 return 方式(一次性加载所有数据到内存)
            Console.WriteLine("1. 测试传统 return 方式(生成100万个整数):");
            var watchReturn = Stopwatch.StartNew(); // 计时
            var memoryBeforeReturn = GetCurrentMemoryMB();

            // 调用传统方法并遍历(即使只遍历前10个,也会先加载全部100万条到内存)
            var numbersReturn = GetNumbersReturn();
            int countReturn = 0;
            foreach (var num in numbersReturn)
            {
                countReturn++;
                if (countReturn >= 10) break; // 只遍历前10个
            }

            watchReturn.Stop();
            var memoryAfterReturn = GetCurrentMemoryMB();
            Console.WriteLine($"   遍历前10个耗时: {watchReturn.Elapsed.TotalMilliseconds:F2} ms");
            Console.WriteLine($"   内存占用变化: +{(memoryAfterReturn - memoryBeforeReturn):F2} MB\n");

            // 手动GC,清理内存(便于对比)
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine($"GC后内存占用: {GetCurrentMemoryMB():F2} MB\n");

            // 测试 yield return 方式(按需生成,用多少生成多少)
            Console.WriteLine("2. 测试 yield return 方式(生成100万个整数):");
            var watchYield = Stopwatch.StartNew();
            var memoryBeforeYield = GetCurrentMemoryMB();

            // 调用yield方法并遍历(只生成并返回前10个,不会加载全部)
            var numbersYield = GetNumbersYield();
            int countYield = 0;
            foreach (var num in numbersYield)
            {
                countYield++;
                if (countYield >= 10) break; // 只遍历前10个
            }

            watchYield.Stop();
            var memoryAfterYield = GetCurrentMemoryMB();
            Console.WriteLine($"   遍历前10个耗时: {watchYield.Elapsed.TotalMilliseconds:F2} ms");
            Console.WriteLine($"   内存占用变化: +{(memoryAfterYield - memoryBeforeYield):F2} MB\n");

            Console.WriteLine("===== 演示结束 =====");
            Console.ReadLine();
        }

        /// <summary>
        /// 传统return:一次性生成所有数据,全部加载到内存
        /// </summary>
        static List<int> GetNumbersReturn()
        {
            var result = new List<int>();
            // 生成100万个整数,全部添加到List(占用大量内存)
            for (int i = 1; i <= 1000000; i++)
            {
                result.Add(i);
            }
            return result; // 一次性返回所有数据
        }

        /// <summary>
        /// yield return:按需生成数据,遍历一个返回一个,几乎不占内存
        /// </summary>
        static IEnumerable<int> GetNumbersYield()
        {
            // 循环到100万,但只在遍历的时候才生成对应的值
            for (int i = 1; i <= 1000000; i++)
            {
                yield return i; // 返回当前值,暂停方法执行;下次遍历从这里继续
            }
        }

        /// <summary>
        /// 获取当前进程的内存占用(MB)
        /// </summary>
        static double GetCurrentMemoryMB()
        {
            // 获取进程的工作集内存(实际占用的物理内存)
            var process = Process.GetCurrentProcess();
            return process.WorkingSet64 / (1024.0 * 1024.0);
        }
    }
}

image


第二部分:完整演变过程(6 个阶段,代码可直接运行)

以 "筛选大于 10 的整数" 为例,展示从"手写循环"到"标准 LINQ"的完整进化。

阶段 1:原始 foreach 循环(最底层,无任何封装)

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        // 数据源:一个整数列表
        List<int> list = new List<int> { 1, 12, 3, 16, 9 };
        
        // 结果集:手动创建 List 存储筛选结果
        List<int> result = new List<int>();

        // 【核心逻辑】手写遍历 + 硬编码条件
        foreach (int num in list)
        {
            // 硬编码:条件写死在代码里,改条件必须改源码
            if (num > 10) 
            {
                result.Add(num); // 符合条件就加入结果集
            }
        }

        // 输出结果:12、16
        Console.WriteLine("筛选结果:");
        foreach (int num in result) 
            Console.WriteLine(num);
    }
}

问题分析:

  • ❌ 零复用性:每个筛选需求都要重写一遍 foreach
  • ❌ 条件硬编码:想筛"偶数"必须改源码,不能动态传入
  • ❌ 立即执行:哪怕只要第 1 个结果,也要遍历全部元素

image

阶段 2:封装成普通方法(实现复用,但条件仍固定)

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int> { 1, 12, 3, 16, 9 };
        
        // 【改进】调用封装方法,不用重复写循环逻辑
        List<int> result = FilterGreaterThan10(list);
        
        foreach (int num in result) 
            Console.WriteLine(num);
    }

    /// <summary>
    /// 封装筛选逻辑:但只能筛选 >10 的数
    /// 问题:条件固定,无法筛选其他条件(如偶数、奇数)
    /// </summary>
    static List<int> FilterGreaterThan10(List<int> list)
    {
        List<int> result = new List<int>();
        
        foreach (int num in list)
        {
            // 条件还是硬编码!换条件要重写方法
            if (num > 10) 
                result.Add(num);
        }
        
        return result;
    }
}

image

改进点:循环逻辑复用了

仍有问题:条件固定,不够通用

阶段 3:传入委托作为条件(实现通用化,核心突破)

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int> { 1, 12, 3, 16, 9 };
        
        // 【突破】调用1:筛选 >10 的数(传入 Lambda 作为条件)
        List<int> result1 = MyWhere(list, num => num > 10);
        
        // 【突破】调用2:筛选偶数(换条件不用改方法,只改 Lambda)
        List<int> result2 = MyWhere(list, num => num % 2 == 0);
        
        Console.WriteLine("大于10的数:");
        foreach (int num in result1) Console.WriteLine(num); // 12、16
        
        Console.WriteLine("偶数:");
        foreach (int num in result2) Console.WriteLine(num); // 12、16
    }

    /// <summary>
    /// 通用筛选方法:通过委托参数接收"筛选条件"
    /// </summary>
    /// <param name="list">要筛选的集合</param>
    /// <param name="condition">筛选条件(Func<int, bool> 是 .NET 内置委托类型)</param>
    /// <returns>筛选后的结果列表</returns>
    static List<int> MyWhere(List<int> list, Func<int, bool> condition)
    {
        List<int> result = new List<int>();
        
        foreach (int num in list)
        {
            // 【关键】执行外部传入的 condition,不再硬编码
            // condition(num) 就是你传入的 Lambda(如 x => x > 10)
            if (condition(num)) 
                result.Add(num);
        }
        
        return result;
    }
}

image

核心突破:

  • ✅ 条件外部化:通过 Func<int, bool> 委托传入条件
  • ✅ 完全通用:一个方法支持任意筛选逻辑
  • ⚠️ 调用方式:MyWhere(list, condition) 不够优雅

阶段 4:改成扩展方法(调用形式优化,接近标准 LINQ)

using System;
using System.Collections.Generic;

// 【关键】扩展方法必须放在静态类中
static class MyLinqExtensions
{
    /// <summary>
    /// 扩展方法:给 List<int> 添加 MyWhere 方法
    /// 核心:参数前加 this 关键字
    /// </summary>
    /// <param name="list">this 表示这是 List<int> 的扩展方法</param>
    /// <param name="condition">筛选条件</param>
    public static List<int> MyWhere(this List<int> list, Func<int, bool> condition)
    {
        List<int> result = new List<int>();
        
        foreach (int num in list)
        {
            if (condition(num))
                result.Add(num);
        }
        
        return result;
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int> { 1, 12, 3, 16, 9 };
        
        // 【优雅】调用形式:从 MyWhere(list, ...) → list.MyWhere(...)
        // 和系统 LINQ 的 list.Where(...) 完全一致!
        var result = list.MyWhere(num => num > 10);
        
        foreach (int num in result) 
            Console.WriteLine(num);
    }
}

image

核心改进:

  • ✅ 调用优雅:list.MyWhere(...) 符合直觉
  • ✅ 链式调用基础:为后续 .Where().Select() 铺路

阶段 5:加入 yield 实现延迟执行(LINQ 的灵魂)

using System;
using System.Collections.Generic;

static class MyLinqExtensions
{
    /// <summary>
    /// 【终极进化】加入 yield 实现延迟执行
    /// 这是 LINQ 高效的核心!
    /// </summary>
    /// <param name="source">改为 IEnumerable<int>,支持所有可枚举类型</param>
    /// <param name="condition">筛选条件</param>
    /// <returns>IEnumerable<int> 迭代器(延迟执行)</returns>
    public static IEnumerable<int> MyWhere(this IEnumerable<int> source, Func<int, bool> condition)
    {
        // 【关键改动1】不再创建 List 存储结果
        // 【关键改动2】遍历到符合条件的元素时,yield return 单个返回
        
        foreach (int num in source)
        {
            if (condition(num))
            {
                // 【灵魂】yield return:延迟返回单个元素
                // 执行到这里:返回 num,方法暂停,等待下一次迭代
                yield return num; 
            }
        }
        // 没有 return 语句,迭代器自动处理结束
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int> { 1, 12, 3, 16, 9 };
        
        // 【延迟执行的关键体现】
        // 此时 MyWhere 并未执行!只是"定义查询"
        var query = list.MyWhere(num => num > 10);
        
        Console.WriteLine("查询已定义(尚未执行)");
        
        // 【触发执行】只有遍历 query 时,MyWhere 才真正执行
        // 执行过程:12(返回)→ 暂停 → 要下一个 → 16(返回)→ 暂停 → 结束
        Console.WriteLine("开始遍历(触发执行):");
        foreach (int num in query) 
            Console.WriteLine(num);
    }
}

延迟执行的威力:

// 场景:百万级数据,只取前 5 个
var hugeList = Enumerable.Range(1, 1000000); // 1 到 100 万

// 传统方式:先筛选全部,再取 5 个(遍历 100 万次)
var traditional = hugeList.Where(x => x % 2 == 0).ToList().Take(5);

// yield 方式:筛选一个取一个,取够 5 个立即停止(只遍历约 10 次)
var deferred = hugeList.MyWhere(x => x % 2 == 0).Take(5);

foreach(var num in deferred) Console.WriteLine(num);

image

阶段 6:标准 LINQ 调用(最终形态)

using System;
using System.Collections.Generic;
using System.Linq; // 引入系统 LINQ 命名空间

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int> { 1, 12, 3, 16, 9 };
        
        // 【最终形态】系统 LINQ 的 Where
        // 底层逻辑和我们手写的 MyWhere 完全一致!
        var result = list.Where(num => num > 10);
        
        foreach (int num in result) 
            Console.WriteLine(num);
    }
}

image


第三部分:Select 手写演变(投影/转换操作)

Select 是 LINQ 的"转换"操作,原理和 Where 相同,只是委托的签名不同。

完整手写版(泛型 + 全注释)

using System;
using System.Collections.Generic;

static class MyLinqExtensions
{
    /// <summary>
    /// 手写 LINQ Select(投影/转换)
    /// 将 TSource 类型的集合转换为 TResult 类型的集合
    /// </summary>
    /// <typeparam name="TSource">源集合元素类型(输入)</typeparam>
    /// <typeparam name="TResult">目标元素类型(输出)</typeparam>
    /// <param name="source">要转换的源集合(this → 扩展方法)</param>
    /// <param name="selector">转换逻辑委托:输入源元素,输出目标元素</param>
    /// <returns>延迟执行的转换结果迭代器</returns>
    public static IEnumerable<TResult> MySelect<TSource, TResult>(
        this IEnumerable<TSource> source, 
        Func<TSource, TResult> selector)
    {
        // 【新手避坑】空值保护(系统 LINQ 也会做)
        if (source == null) 
            throw new ArgumentNullException(nameof(source), "源集合不能为 null");
        if (selector == null) 
            throw new ArgumentNullException(nameof(selector), "转换逻辑不能为 null");

        // 遍历源集合
        foreach (TSource item in source)
        {
            // 执行外部传入的转换逻辑,yield return 延迟返回结果
            // 例如:num => $"数字:{num}",把 int 转成 string
            yield return selector(item);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<int> list = new List<int> { 1, 2, 3 };
        
        // 转换逻辑:数字 → 字符串(1 → "数字:1")
        var result = list.MySelect(num => $"数字:{num}");
        
        // 遍历触发执行,输出:数字:1、数字:2、数字:3
        foreach (var str in result) 
            Console.WriteLine(str);
        
        // 更复杂的转换:数字 → 匿名对象
        var objects = list.MySelect(num => new { 
            Original = num, 
            Squared = num * num 
        });
        
        foreach (var obj in objects)
            Console.WriteLine($"原值:{obj.Original},平方:{obj.Squared}");
    }
}

image


第四部分:完整泛型版 MyLinq 类(生产级代码)

整合 WhereSelect,加入完整空值保护、XML 注释,完全模拟系统 LINQ 的核心逻辑。

using System;
using System.Collections.Generic;

/// <summary>
/// 手写 LINQ 核心方法库(模拟 System.Linq 底层实现)
/// 包含 Where(筛选)、Select(转换),支持泛型 + 延迟执行
/// </summary>
public static class MyLinq
{
    #region Where 筛选操作
    
    /// <summary>
    /// 筛选集合中符合条件的元素(延迟执行)
    /// 底层实现:foreach + yield return
    /// </summary>
    /// <typeparam name="T">集合元素类型</typeparam>
    /// <param name="source">要筛选的源集合</param>
    /// <param name="predicate">筛选条件(谓词):输入元素,返回 bool 表示是否符合</param>
    /// <returns>符合条件的元素迭代器(延迟执行)</returns>
    /// <exception cref="ArgumentNullException">源集合或条件为 null 时抛出</exception>
    /// <example>
    /// var result = list.MyWhere(x => x > 10);
    /// </example>
    public static IEnumerable<T> MyWhere<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
        // 【防御式编程】空值检查(系统 LINQ 也会抛出异常)
        if (source == null)
            throw new ArgumentNullException(nameof(source), "源集合不能为 null");
        if (predicate == null)
            throw new ArgumentNullException(nameof(predicate), "筛选条件不能为 null");

        // 【核心逻辑】遍历源集合,延迟返回符合条件的元素
        foreach (T item in source)
        {
            // predicate 就是外部传入的 Lambda(如 x => x > 10)
            if (predicate(item))
            {
                // 【关键】yield return 实现延迟执行
                // 用一个取一个,不一次性加载所有结果到内存
                yield return item;
            }
        }
    }
    
    #endregion

    #region Select 转换操作
    
    /// <summary>
    /// 将集合中的每个元素转换为指定类型(延迟执行)
    /// 底层实现:foreach + yield return
    /// </summary>
    /// <typeparam name="TSource">源元素类型(输入)</typeparam>
    /// <typeparam name="TResult">目标元素类型(输出)</typeparam>
    /// <param name="source">要转换的源集合</param>
    /// <param name="selector">转换逻辑:输入源元素,输出目标元素</param>
    /// <returns>转换后的元素迭代器(延迟执行)</returns>
    /// <exception cref="ArgumentNullException">源集合或转换逻辑为 null 时抛出</exception>
    /// <example>
    /// var strings = numbers.MySelect(x => x.ToString());
    /// </example>
    public static IEnumerable<TResult> MySelect<TSource, TResult>(
        this IEnumerable<TSource> source, 
        Func<TSource, TResult> selector)
    {
        // 空值检查
        if (source == null)
            throw new ArgumentNullException(nameof(source), "源集合不能为 null");
        if (selector == null)
            throw new ArgumentNullException(nameof(selector), "转换逻辑不能为 null");

        // 遍历源集合,执行转换并延迟返回
        foreach (TSource item in source)
        {
            // selector 就是外部传入的转换逻辑(如 x => x * 2)
            yield return selector(item);
        }
    }
    
    #endregion
}

// ==================== 测试验证 ====================
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("========== MyLinq 测试 ==========\n");
        
        // 测试数据
        List<int> nums = new List<int> { 1, 12, 3, 16, 9, 20, 5 };
        
        Console.WriteLine("原始数据:1, 12, 3, 16, 9, 20, 5\n");

        // 测试 1:单独使用 MyWhere
        Console.WriteLine("【测试1】筛选 >10 的数:");
        var filtered = nums.MyWhere(x => x > 10);
        foreach (int n in filtered) Console.Write($"{n} "); // 12 16 20
        Console.WriteLine("\n");

        // 测试 2:单独使用 MySelect
        Console.WriteLine("【测试2】所有数乘以 2:");
        var doubled = nums.MySelect(x => x * 2);
        foreach (int n in doubled) Console.Write($"{n} "); // 2 24 6 32 18 40 10
        Console.WriteLine("\n");

        // 测试 3:链式调用(和系统 LINQ 完全一致)
        Console.WriteLine("【测试3】链式调用:先筛选 >10,再乘以 2:");
        var pipeline = nums
            .MyWhere(x => x > 10)   // 筛选:12, 16, 20
            .MySelect(x => x * 2);  // 转换:24, 32, 40
        
        foreach (int n in pipeline) Console.Write($"{n} "); // 24 32 40
        Console.WriteLine("\n");

        // 测试 4:延迟执行验证
        Console.WriteLine("【测试4】延迟执行验证:");
        var deferredQuery = nums.MyWhere(x => {
            Console.WriteLine($"  [正在检查 {x}]");
            return x > 10;
        });
        
        Console.WriteLine("查询已定义,尚未执行(没有输出)");
        Console.WriteLine("开始遍历(触发执行):");
        int count = 0;
        foreach (int n in deferredQuery)
        {
            Console.WriteLine($"  >> 符合条件:{n}");
            if (++count >= 2) break; // 只取前 2 个,验证是否提前终止
        }
        Console.WriteLine("注意:只检查了部分元素就停止,证明是延迟执行\n");

        // 测试 5:空值检查
        Console.WriteLine("【测试5】空值检查:");
        try
        {
            List<int> nullList = null;
            var _ = nullList.MyWhere(x => x > 10);
        }
        catch (ArgumentNullException ex)
        {
            Console.WriteLine($"捕获异常:{ex.ParamName} - {ex.Message}");
        }
        
        Console.WriteLine("\n========== 测试完成 ==========");
    }
}

运行结果:

========== MyLinq 测试 ==========

原始数据:1, 12, 3, 16, 9, 20, 5

【测试1】筛选 >10 的数:
12 16 20 

【测试2】所有数乘以 2:
2 24 6 32 18 40 10 

【测试3】链式调用:先筛选 >10,再乘以 2:
24 32 40 

【测试4】延迟执行验证:
查询已定义,尚未执行(没有输出)
开始遍历(触发执行):
  [正在检查 1]
  [正在检查 12]
  >> 符合条件:12
  [正在检查 3]
  [正在检查 16]
  >> 符合条件:16
注意:只检查了部分元素就停止,证明是延迟执行

【测试5】空值检查:
捕获异常:source - 源集合不能为 null

========== 测试完成 ==========

image


第五部分:常见 LINQ 操作速查表

操作 作用 手写核心逻辑 使用示例 Where 筛选 if (predicate(item)) yield return item; list.Where(x => x > 10) Select 转换 yield return selector(item); list.Select(x => x * 2) First 取首个 foreach (...) return item; throw; list.First(x => x > 10) Count 计数 int count=0; foreach (...) count++; return count; list.Count(x => x > 10) Any 是否存在 foreach (...) if (predicate) return true; return false; list.Any(x => x > 10) Take 取前 N 个 int i=0; foreach (...) { if (i++ >= n) yield break; yield return item; } list.Take(5)


第六部分:总结与学习路径

6.1 核心知识点回顾

LINQ 底层本质 = foreach 循环 + 委托(predicate/selector) + 扩展方法 + yield 迭代器
                     ↑              ↑                    ↑              ↑
                  遍历基础       条件/转换外部化         调用优雅化      延迟执行核心

6.2 演变路线图

阶段1: 手写硬编码循环
    ↓ 发现问题:重复写循环
阶段2: 封装成方法
    ↓ 发现问题:条件固定,不通用
阶段3: 传入委托作为条件
    ↓ 发现问题:调用方式不够优雅(MyWhere(list, condition))
阶段4: 改成扩展方法
    ↓ 发现问题:立即执行,占用内存
阶段5: 加入 yield 实现延迟执行
    ↓ 优化:加入泛型、空值检查
阶段6: 完整泛型版 MyLinq 类
    ↓ 日常使用
阶段7: 系统 LINQ(System.Linq)

6.3 新手学习建议

  1. 先跑通代码:复制本文代码到 VS,单步调试看执行流程
  2. 理解 yield:这是最难的概念,建议手写对比 returnyield return 的内存占用
  3. 查看源码:用 ILSpy 或 VS 反编译看系统 System.Linq.Enumerable 的实现(和你写的一模一样)
  4. 实践链式调用:尝试手写 Where + Select + OrderBy 的组合

6.4 关键结论

  1. 没有黑魔法:LINQ 底层就是 foreach + 委托 + yield,你完全可以自己实现
  2. 延迟执行是灵魂:yield return 让 LINQ 能处理大数据而不爆内存
  3. 扩展方法是语法糖:让调用更优雅,但核心逻辑不变
  4. 泛型是通用保障:MyWhere<T>MyWhere(int) 强大 100 倍

延伸阅读:理解本文后,建议学习 IQueryable<T>(LINQ to SQL 的延迟执行原理)和表达式树(Expression<Func<T,bool>>),这是 LINQ 进阶的核心。


👋 关注我!持续分享 C# 实战技巧、代码示例 & 技术干货

  • 获取示例代码,轻松上手!

  • 私信输入数字: 5xqb20

  • 获取代码下载链接

    image