IEnumerable for vs foreach
IEnumerable for vs foreach
我有一种方法可以产生 returns 值。例如:
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 循环并避免由 ElementAt(i)
引起的多次调用?或者...我可以通过其索引从 IEnumerable 中调用一个元素,而不会导致对其先前元素的迭代吗?我唯一找到的是 this,但是
for (int i = 0; i < 10; i++)
{
Console.WriteLine(GetValues().Skip(i).First());
}
也不行。
IEnumerable
没有索引器。如果您按照自己的方式使用 for 循环,它将针对每个 i
遍历 IEnumerable
。
一个好的解决方案是 foreach 循环。如果您需要一个索引,您可以使用计数器对每个循环迭代进行计数。
如果你想通过索引访问项目并减少 GetValues
调用,你必须具体化惰性枚举,它由 GetValues
:
产生
var values = GetValues()
.ToArray();
for (int i = 0; i < values.Length; i++)
{
Console.WriteLine(values[i]);
}
否则这个:
GetValues().Skip(i).First()
将一次又一次地创建一个新的惰性枚举器。
如果您 return 一个 IList<int>
(f.e 一个 int[]
或 List<int>
),Enumerable.ElementAt
使用索引器而不是枚举所有.
public static IList<int> GetValues()
{
int[] ints = Enumerable.Range(0, 10).ToArray();
return ints;
}
但这并没有使用延迟执行,因此您要将所有内容加载到内存中。
您不能向后移动或引用 IEnumerable<> 对象中的随机索引 - 集合可以通过多种方式创建,包括随机性,并且没有神奇的方法可以在不遍历所有先前元素的情况下获取第 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<>对象——你可以通过调用
来创建一个
.ToList()
另一个回复中提到的.ToArray()
其实有点慢,在做成数组之前内部调用.ToList()
(因为数组需要有固定的大小,我们不知道IEnumerable 中的元素数,直到我们枚举到最后)
您可以创建自己的惰性代理 class,它只会在需要时枚举枚举器
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
我有一种方法可以产生 returns 值。例如:
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 循环并避免由 ElementAt(i)
引起的多次调用?或者...我可以通过其索引从 IEnumerable 中调用一个元素,而不会导致对其先前元素的迭代吗?我唯一找到的是 this,但是
for (int i = 0; i < 10; i++)
{
Console.WriteLine(GetValues().Skip(i).First());
}
也不行。
IEnumerable
没有索引器。如果您按照自己的方式使用 for 循环,它将针对每个 i
遍历 IEnumerable
。
一个好的解决方案是 foreach 循环。如果您需要一个索引,您可以使用计数器对每个循环迭代进行计数。
如果你想通过索引访问项目并减少 GetValues
调用,你必须具体化惰性枚举,它由 GetValues
:
var values = GetValues()
.ToArray();
for (int i = 0; i < values.Length; i++)
{
Console.WriteLine(values[i]);
}
否则这个:
GetValues().Skip(i).First()
将一次又一次地创建一个新的惰性枚举器。
如果您 return 一个 IList<int>
(f.e 一个 int[]
或 List<int>
),Enumerable.ElementAt
使用索引器而不是枚举所有.
public static IList<int> GetValues()
{
int[] ints = Enumerable.Range(0, 10).ToArray();
return ints;
}
但这并没有使用延迟执行,因此您要将所有内容加载到内存中。
您不能向后移动或引用 IEnumerable<> 对象中的随机索引 - 集合可以通过多种方式创建,包括随机性,并且没有神奇的方法可以在不遍历所有先前元素的情况下获取第 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<>对象——你可以通过调用
来创建一个.ToList()
另一个回复中提到的.ToArray()
其实有点慢,在做成数组之前内部调用.ToList()
(因为数组需要有固定的大小,我们不知道IEnumerable 中的元素数,直到我们枚举到最后)
您可以创建自己的惰性代理 class,它只会在需要时枚举枚举器
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