我有一个产生返回值的方法。 e.g:
public static IEnumerable<int> GetValues()
{
for (int i = 0; i < 10; i++)
{
yield return i;
}
}
当我用foreach调用此方法时,yield return i;
被调用10次
foreach (int i in GetValues())
{
Console.WriteLine(i);
}
当我用for循环调用它时,yield return i;
被称为阶乘10次
for (int i = 0;i< 10;i++)
{
Console.WriteLine(GetValues().ElementAt(i));
}
问题:有没有办法保持for-loop并避免由ElementAt(i)
引起的那些多次调用?或者......我可以通过其索引从IEnumerable中调用一个元素,而不会导致其先前元素的迭代吗?我发现的唯一的事情是this,但是
for (int i = 0; i < 10; i++)
{
Console.WriteLine(GetValues().Skip(i).First());
}
也不起作用。
答案 0 :(得分:7)
如果您想按索引访问项目并减少GetValues
次调用,则必须实现由GetValues
生成的延迟可枚举:
var values = GetValues()
.ToArray();
for (int i = 0; i < values.Length; i++)
{
Console.WriteLine(values[i]);
}
否则:
GetValues().Skip(i).First()
将一次又一次地创建一个新的惰性枚举器。
答案 1 :(得分:6)
您无法向后移动或引用IEnumerable中的随机索引&lt;&gt;对象 - 可以通过各种方式创建集合,包括随机性,并且没有获得第n个元素的神奇方法,而不会迭代所有先前的元素。
IEnumerable<>
的常见用法是:
foreach (var value in GetValues())
{
Console.WriteLine(value);
}
转换为:
using (var enumerator = GetValues().GetEnumerator())
{
while(enumerator.MoveNext())
{
var value = enumerator.Current;
Console.WriteLine(value);
}
}
如果要引用特定索引,则需要IList&lt;&gt;对象 - 您可以通过调用
创建一个 .ToList()
另一个响应中提到的.ToArray()
实际上有点慢,并且在将其设置为数组之前在内部调用.ToList()
(因为数组需要具有固定大小而我们不知道IEnumerable中的元素数量,直到我们枚举到最后)
您可以创建自己的代理,lazy类,仅在需要时枚举枚举器
public static IEnumerable<int> GetValues()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("yielding " + i);
yield return i;
}
}
class LazyList<T>
{
IEnumerator<T> enumerator;
IList<T> list;
public LazyList(IEnumerable<T> enumerable)
{
enumerator = enumerable.GetEnumerator();
list = new List<T>();
}
public T this[int index]
{
get
{
while (list.Count <= index && enumerator.MoveNext())
{
list.Add(enumerator.Current);
}
return list[index];
}
}
}
static void Main(string[] args)
{
var lazy = new LazyList<int>(GetValues());
Console.WriteLine(lazy[0]);
Console.WriteLine(lazy[4]);
Console.WriteLine(lazy[2]);
Console.WriteLine(lazy[1]);
Console.WriteLine(lazy[7]);
Console.WriteLine(lazy[9]);
Console.WriteLine(lazy[6]);
Console.Read();
}
将产生:
yielding 0
0
yielding 1
yielding 2
yielding 3
yielding 4
4
2
1
yielding 5
yielding 6
yielding 7
7
yielding 8
yielding 9
9
6
答案 2 :(得分:1)
IEnumerable
没有索引器。如果您按照自己的方式使用for循环,则会针对每个IEnumerable
迭代i
。
一个好的解决方案是foreach循环。如果您需要索引,则可以使用计数器对每个循环迭代进行计数。
答案 3 :(得分:1)
如果您返回IList<int>
(f.e an int[]
或List<int>
),则Enumerable.ElementAt
会使用索引器而非枚举所有。
public static IList<int> GetValues()
{
int[] ints = Enumerable.Range(0, 10).ToArray();
return ints;
}
但这并不是使用延迟执行,因此您需要将所有内容加载到内存中。