连续调用像 Count() 这样的方法是否会重新枚举 IEnumerable<T>?
Does successive calls to a method like Count() reenumerate an IEnumerable<T>?
我对 IEnumerable 有点困惑,它是延迟执行行为。
假设我有以下 IEnumerable<T>
:
IEnumerable<Foo> enumerable = GetFoos();
如果我这样做会怎样:
int count = enumerable.Count();
count = enumerable.Count();
我能想到三种可能:
a) 将再次枚举集合
b) 将缓存结果以供第二次使用(如延迟加载)
c) 将取决于在 GetFoos()
方法中实例化的下划线类型以及它如何实现 IEnumerable 接口
哪一个是正确的?另外,如果 c
是正确的,那么使用 yield return
创建的 IEnumerable 会发生什么?
对 referencesourcecode 的快速检查给出了 .Count<T>(this IEnumerable<T>)
扩展名的以下定义(简化):
免责声明您应该不依赖该实现,也不期望该实现总是以某种方式做某事。
public static int Count<TSource>(this IEnumerable<TSource> source) {
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator()) {
while (e.MoveNext()) count++;
}
return count;
}
所以答案是 (c),这取决于基础类型。如果它不是 ICollection
类型,则 IEnumerable
将被第二次计算(即 GetEnumerator
将被调用并且 Enumerator 将被循环。
那么使用 yield
语法会发生什么?
好吧yield
只是实现GetEnumerator
的一种奇特方式,所以当调用Count()
两次时会发生什么,这个伪GetEnumerator
方法将被调用两次。我认为一个代码片段可以说一千多个字:
private static IEnumerable<int> ConstantEnumerable()
{
yield return 1;
yield return 2;
yield return 3;
}
private static int i = 0;
private static IEnumerable<int> ChangingEnumerable()
{
if (i == 0)
{
yield return 1;
i++;
}
else
{
yield return 2;
yield return 3;
}
}
public static void Main()
{
var constant = ConstantEnumerable();
var changing = ChangingEnumerable();
Console.WriteLine("Constant: {0}, {1}", constant.Count(), constant.Count()); // 3, 3
Console.WriteLine("Changing: {0}, {1}", changing.Count(), changing.Count()); // 1, 2
}
我对 IEnumerable 有点困惑,它是延迟执行行为。
假设我有以下 IEnumerable<T>
:
IEnumerable<Foo> enumerable = GetFoos();
如果我这样做会怎样:
int count = enumerable.Count();
count = enumerable.Count();
我能想到三种可能:
a) 将再次枚举集合
b) 将缓存结果以供第二次使用(如延迟加载)
c) 将取决于在 GetFoos()
方法中实例化的下划线类型以及它如何实现 IEnumerable 接口
哪一个是正确的?另外,如果 c
是正确的,那么使用 yield return
创建的 IEnumerable 会发生什么?
对 referencesourcecode 的快速检查给出了 .Count<T>(this IEnumerable<T>)
扩展名的以下定义(简化):
免责声明您应该不依赖该实现,也不期望该实现总是以某种方式做某事。
public static int Count<TSource>(this IEnumerable<TSource> source) {
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator()) {
while (e.MoveNext()) count++;
}
return count;
}
所以答案是 (c),这取决于基础类型。如果它不是 ICollection
类型,则 IEnumerable
将被第二次计算(即 GetEnumerator
将被调用并且 Enumerator 将被循环。
那么使用 yield
语法会发生什么?
好吧yield
只是实现GetEnumerator
的一种奇特方式,所以当调用Count()
两次时会发生什么,这个伪GetEnumerator
方法将被调用两次。我认为一个代码片段可以说一千多个字:
private static IEnumerable<int> ConstantEnumerable()
{
yield return 1;
yield return 2;
yield return 3;
}
private static int i = 0;
private static IEnumerable<int> ChangingEnumerable()
{
if (i == 0)
{
yield return 1;
i++;
}
else
{
yield return 2;
yield return 3;
}
}
public static void Main()
{
var constant = ConstantEnumerable();
var changing = ChangingEnumerable();
Console.WriteLine("Constant: {0}, {1}", constant.Count(), constant.Count()); // 3, 3
Console.WriteLine("Changing: {0}, {1}", changing.Count(), changing.Count()); // 1, 2
}