gpt4 book ai didi

c# - Linq 的 IEnumerable.Select 是否返回对原始 IEnumerable 的引用?

转载 作者:太空宇宙 更新时间:2023-11-03 18:53:03 30 4
gpt4 key购买 nike

我试图在我的代码中克隆一个列表,因为我需要将该列表输出到其他代码,但原始引用稍后将被清除。所以我有了使用 Select 扩展方法来创建对相同元素的 IEnumerable 的新引用的想法,例如:

List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => s);

在执行 ogList.Clear() 之后,我惊讶地发现我的新枚举也是空的。

所以我开始在 LINQPad 中摆弄,发现即使我的 Select 完全返回不同的对象,行为也是相同的。

List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => 5); // Doesn't return the original int
enumerable.Count().Dump(); // Count is 3
ogList.Clear();
enumerable.Count().Dump(); // Count is 0!

请注意,在 LINQPad 中,Dump() 等同于 Console.WriteLine()

现在我首先需要克隆列表的原因可能是设计不当,即使我不想重新考虑设计,我也可以轻松地正确克隆它。但这让我开始思考 Select 扩展方法实际上做了什么

根据documentation对于选择:

This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.

然后我尝试在清除之前添加这段代码:

foreach (int i in enumerable)
{
i.Dump();
}

结果还是一样。

最后,我尝试了最后一件事来弄清楚我的新枚举中的引用是否与旧的相同。我没有清除原始列表,而是:

ogList.Add(4);

然后我打印出我的可枚举(“克隆”的)的内容,希望看到“4”附加到它的末尾。相反,我得到了:

5
5
5
5 // Huh?

现在我别无选择,只能承认我不知道 Select 扩展方法在幕后是如何工作的。怎么回事?

最佳答案

List/List<T>出于所有意图和目的,花哨的可调整大小的数组。他们拥有并保存值类型的数据,例如您的整数或对内存中引用类型数据的引用,并且他们始终知道他们有多少项。

IEnumerable/IEnumerable<T>是不同的野兽。他们提供不同的服务/契约(Contract)。一个IEnumerable是虚构的,不存在的。它可以凭空创建数据,无需物理支持。他们唯一的 promise 是他们有一个名为 GetEnumerator() 的公共(public)方法。返回 IEnumerator/IEnumerator<T> . IEnumerator的 promise 制作很简单:当您决定需要时,某些项目可能可用或不可用。这是通过一个简单的方法实现的,即 IEnumerator界面有:bool MoveNext() - 当枚举完成时返回 false,如果实际上有一个新项目需要返回则返回 true。您可以通过 IEnumerator 的属性读取数据接口(interface)有,方便叫Current .

回到您的观察/问题:至于 IEnumerable在您的示例中,它甚至不会考虑数据,除非您的代码告诉它获取一些数据。

写作时:

List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => s);

你是说:听这里IEnumerable ,我可能会在将来的某个时候来找你要一些元素。我会告诉你我什么时候需要它们,因为现在坐着不动什么都不做。与 Select(s => s)您在概念上定义了 int 到 int 的恒等投影。

您编写的选择的一个非常粗略的简化的非现实实现是:

IEnumerable<T> Select(this IEnumerable<int> source, Func<int,T> transformer) something like
{
foreach (var i in source) //create an enumerator for source and starts enumeration
{
yield return transformer(i); //yield here == return an item and wait for orders
}
}

(这解释了为什么你在期待 for 时得到了 5,你的转换是 s => 5)

对于值类型,例如您的情况下的整数:如果要克隆列表,请使用通过 List 实现的枚举结果克隆整个列表或其中的一部分以供将来枚举。 .通过这种方式,您可以创建一个列表,该列表是原始列表的克隆,完全与其原始列表分离:

IEnumerable<int> cloneOfEnumerable = ogList.Select(s => s).ToList();

稍后编辑:当然 ogList.Select(s => s) 等同于 ogList。我将把投影留在这里,因为它在问题中。

您在这里创建的是:来自可枚举结果的列表,通过 IEnumerable<int> 进一步消耗。界面。考虑到我上面所说的关于 IList 的性质对比IEnumerable ,我更愿意写/读:

IList<int> cloneOfEnumerable = ogList.ToList();

警告:小心引用类型。 IList/List不 promise 保持对象“安全”,它们可以为所有 IList 突变为 null关心。如果您需要关键字:深度克隆。

注意:当心无限或不可倒带的 IEnumerables

关于c# - Linq 的 IEnumerable.Select 是否返回对原始 IEnumerable 的引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52450472/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com