You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LINQ Enumerable查询返回副本还是引用?C++复刻Skip/Take疑问

关于LINQ Enumerable查询及Skip/Take方法的疑问解答

问题1:LINQ Enumerable查询返回的是副本还是引用?

这个得分情况讨论,核心是LINQ返回的IEnumerable<T>是延迟执行的——它本身不存储数据,只是记录了查询逻辑,只有当你真正枚举它(比如用foreach、ToList()、ToArray())时才会遍历原数据源:

  • 如果T是引用类型(比如自定义的Person类),枚举时拿到的是原数据源中对象的引用——修改这些对象的属性,原数据源里的对应对象也会跟着变化。
  • 如果T是值类型(比如int、DateTime),枚举时拿到的是原数据的副本——修改这些值不会影响原数据源里的内容。

举个直观的例子:

// 引用类型示例
var people = new List<Person> { new Person { Name = "Alice" } };
var query = people.Where(p => p.Name == "Alice");
foreach (var p in query)
{
    p.Name = "Bob"; // 原列表里的Alice也会变成Bob
}

// 值类型示例
var numbers = new List<int> { 1, 2, 3 };
var numQuery = numbers.Where(n => n > 1);
foreach (var n in numQuery)
{
    var modified = n + 1; // 原列表里的2、3不会有任何变化
}

问题2:C#的Skip和Take是返回迭代器还是全新的IEnumerable?

你找到的源码已经给出了关键答案——Skip方法返回的是SkipIterator<TSource>,这正是LINQ里的迭代器实现。

具体来说:

  • Skip和Take都不会立刻创建全新的集合或者拷贝数据,它们返回的是一个实现了IEnumerable<T>的迭代器对象,这个对象只记录了“要跳过N个元素”或者“要取N个元素”的逻辑,以及对原数据源的引用。
  • 只有当你枚举这个返回的IEnumerable<T>时,它才会遍历原数据源,执行跳过/取元素的操作,此时的引用/副本规则和第一个问题里的逻辑一致。
  • 如果你调用slice.ToList()这类方法,才会分配新内存,把筛选后的元素拷贝进去(属于浅拷贝:引用类型存引用,值类型存副本)。

如果你想在C++里复刻这个逻辑,不用一开始就创建新的std::vector,可以实现类似的迭代器适配器——让skip和take返回一个包装了原容器迭代器的对象,当你真正遍历这个适配器时,才会执行跳过/取元素的操作,这样更贴近C# LINQ的延迟执行特性。

内容的提问来源于stack exchange,提问作者Clément Jean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 11:12:40