【发布时间】:2019-02-26 06:16:55
【问题描述】:
我试图在我的代码中克隆一个 List,因为我需要将该 List 输出到其他代码,但原始引用将在稍后被清除。所以我有了使用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()s 等效于 Console.WriteLine()。
现在可能我首先需要克隆列表是由于糟糕的设计,即使我不想重新考虑设计,我也可以轻松正确地克隆它。但这让我想到了 Select 扩展方法实际上做了什么。
根据documentation为Select:
这个方法是通过延迟执行来实现的。立即返回值是一个存储执行操作所需的所有信息的对象。在通过直接调用其 GetEnumerator 方法或使用 Visual C# 中的 foreach 或 Visual Basic 中的 For Each 枚举对象之前,不会执行此方法表示的查询。
然后我尝试在清除之前添加此代码:
foreach (int i in enumerable)
{
i.Dump();
}
结果还是一样。
最后,我尝试了最后一件事来确定我的新枚举中的引用是否与旧枚举相同。我没有清除原始列表,而是这样做了:
ogList.Add(4);
然后我打印出我的可枚举(“克隆”的)的内容,期望在它的末尾看到“4”。相反,我得到了:
5
5
5
5 // Huh?
现在我别无选择,只能承认我不知道 Select 扩展方法在幕后是如何工作的。怎么回事?
【问题讨论】:
-
Select本身并没有实现任何东西;foreach或Enumerator你最终会做的遍历。 -
谁对这个问题和其中一个答案投了反对票,至少给出一个理由?