IEnumerable for vs foreach

fub*_*ubo -1 c# foreach for-loop yield

我有一个产生返回值的方法.例如:

public static IEnumerable<int> GetValues()
{
    for (int i = 0; i < 10; i++)
    {
        yield return i;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我用foreach调用这个方法时,yield return i;被调用了10次

foreach (int i in GetValues())
{
    Console.WriteLine(i);
}
Run Code Online (Sandbox Code Playgroud)

当我用for循环调用它时,yield return i;被称为阶乘10次

for (int i = 0;i< 10;i++)
{
    Console.WriteLine(GetValues().ElementAt(i));
}
Run Code Online (Sandbox Code Playgroud)

问题:有没有办法保持for-loop并避免那些多次调用ElementAt(i)?或者......我可以通过其索引从IEnumerable中调用一个元素,而不会导致其先前元素的迭代吗?我发现的唯一的事情就是这个,但是

for (int i = 0; i < 10; i++)
{
    Console.WriteLine(GetValues().Skip(i).First());
}
Run Code Online (Sandbox Code Playgroud)

也不起作用.

Den*_*nis 7

如果要按索引访问项目并减少GetValues调用,则必须实现延迟枚举,这由GetValues以下内容生成:

var values = GetValues()
    .ToArray();

for (int i = 0; i < values.Length; i++)
{
    Console.WriteLine(values[i]);
}
Run Code Online (Sandbox Code Playgroud)

否则这个:

GetValues().Skip(i).First()
Run Code Online (Sandbox Code Playgroud)

将一次又一次地创建一个新的惰性枚举器.


Ada*_*sko 6

你无法向后移动或引用IEnumerable <>对象中的随机索引 - 可以通过各种方式创建集合,包括随机性,并且没有神奇的方法来获取第n个元素而不迭代所有先前的元素.

常见的用法IEnumerable<>是:

foreach (var value in GetValues())
{
    Console.WriteLine(value);
}
Run Code Online (Sandbox Code Playgroud)

这意味着:

using (var enumerator = GetValues().GetEnumerator())
{
    while(enumerator.MoveNext())
    {
        var value = enumerator.Current;
        Console.WriteLine(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果要引用特定索引,则需要有一个IList <>对象 - 您可以通过调用创建一个

.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();
    }
Run Code Online (Sandbox Code Playgroud)

将产生:

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
Run Code Online (Sandbox Code Playgroud)