此方法通过使用延迟执行来实现.立即返回值是一个对象,该对象存储执行操作所需的所有信息.在通过直接调用对象的GetEnumerator方法或在Visual C#中使用foreach或在Visual Basic中使用For Each枚举对象之前,不会执行此方法表示的查询.
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.
因此,然后我尝试在清除之前添加以下代码:
So then I tried adding this code before clearing:
foreach (int i in enumerable)
{
i.Dump();
}
结果仍然相同.
最后,我尝试了最后一件事,以确定新枚举中的引用是否与旧引用相同.我没有清除原始列表,而是这样做了:
Finally, I tried one last thing to figure out if the reference in my new enumerable was the same as the old one. Instead of clearing the original List, I did:
ogList.Add(4);
然后我打印出我的枚举(克隆"的)的内容,希望在其末尾看到"4".相反,我得到了:
Then I printed out the contents of my enumerable (the "cloned" one), expecting to see '4' appended to the end of it. Instead, I got:
5
5
5
5 // Huh?
现在,我别无选择,只能承认我不知道Select扩展方法在后台如何工作.发生了什么事?
Now I have no choice but to admit that I have no idea how the Select extension method works behind the scenes. What's going on?
推荐答案
List/List< T>
出于所有意图和目的,都是可调整大小的数组.他们拥有并保存值类型的数据(例如您的整数)或对内存中引用类型的数据的引用,并且他们始终知道它们有多少个项目.
List/List<T>
are for all intents and purposes fancy resizable arrays. They own and hold the data for value types such as your ints or references to the data for reference types in memory and they always know how many items they have.
IEnumerable/IEnumerable< T>
是不同的野兽.他们提供不同的服务/合同. IEnumerable
是虚构的,它不存在.它可以在没有物理支持的情况下凭空创建数据.他们唯一的保证就是拥有一个名为 GetEnumerator()
的公共方法,该方法返回 IEnumerator/IEnumerator< T>
. IEnumerator
做出的承诺很简单:当您决定需要某个项目时,某些项目可能可用或不可用.这是通过 IEnumerator
接口具有的简单方法实现的: bool MoveNext()
-在枚举完成时返回false,或者在实际上有新项目时返回true需要退还的.您可以通过 IEnumerator
接口具有的属性(通常称为 Current
.)来读取数据.
IEnumerable/IEnumerable<T>
are different beasts. They provide a different service/contract. An IEnumerable
is fictional, it does not exist. It can create data out of thin air, with no physical backing. Their only promise is that they have a public method called GetEnumerator()
that returns an IEnumerator/IEnumerator<T>
. The promise that an IEnumerator
makes is simple:
some item could be available or not at a time when you decide you need it. This is achieved through a simple method that the IEnumerator
interface has: bool MoveNext()
- which returns false when the enumeration is completed or true if there was in fact a new item that needed to be returned. You can read the data through a property that the IEnumerator
interface has, conveniently called Current
.
回到您的观察/问题:就示例中的 IEnumerable
而言,除非您的代码告诉它获取某些数据,否则它甚至不会考虑数据.
To get back to your observations/question: as far as the IEnumerable
in your example is concerned, it does not even think about the data unless your code tells it to fetch some data.
写作时:
List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => s);
您的意思是:请在此处听 IEnumerable
,我可能会在将来某个时候向您询问一些物品.我会告诉您何时需要它们,暂时不动,不做任何事情.使用 Select(s => s)
从概念上定义从int到int的恒等投影.
You are saying: Listen here IEnumerable
, I might come to you asking for some items at some point in the future. I'll tell you when I will need them, for now sit still and do nothing. With Select(s => s)
you are conceptually defining an identity projection of int to int.
您编写的选择的一个非常粗略的,简化的,非现实的实现是:
A very rough simplified, non-real-life implementation of the select you've written is:
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
}
}
(这解释了为什么您期望a为5时,您的变换为s => 5)
(this explains why you got a 5 when expecting a for, your transform was s => 5)
对于值类型(例如您所用的整数):如果要克隆列表,请使用通过 List实现的枚举结果来克隆整个列表或列表的一部分,以供将来进行枚举代码>.这样,您可以创建一个列表,该列表是原始列表的克隆,并且与原始列表完全分离:
For value types, such as the ints in your case: If you want to clone the list, clone the whole list or part of it for future enumeration by using the result of an enumeration materialized through a List
. This way you create a list that is a clone of the original list, entirely detached from its original list:
IEnumerable<int> cloneOfEnumerable = ogList.Select(s => s).ToList();
稍后当然ogList.Select(s => s)等同于ogList.我要把投影留在这里,就像问题所在一样.
Later edit: Of course ogList.Select(s => s) is equivalent to ogList. I'm leaving the projection here, as it was in the question.
您要在此处创建的是:可枚举结果的列表,该列表通过 IEnumerable< int>
接口进一步使用.考虑到我上面所说的关于 IList
与 IEnumerable
的本质,我更喜欢写/读:
What you are creating here is: a list from the result of an enumerable, further consumed through the IEnumerable<int>
interface. Considering what I've said above about the nature of IList
vs IEnumerable
, I would prefer to write/read:
IList<int> cloneOfEnumerable = ogList.ToList();
注意:注意参考类型. IList/List
不保证对象安全",它们可以在所有 IList
关心的情况下变异为null.关键字(如果需要):深度克隆.
CAUTION: Be careful with reference types. IList/List
make no promise of keeping the objects "safe", they can mutate to null for all IList
cares. Keyword if you ever need it: deep cloning.
注意:当心无限或不可倒退的IEnumerables
CAUTION: Beware of infinite or non-rewindable IEnumerables
这篇关于Linq的IEnumerable.Select是否返回对原始IEnumerable的引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!