C# IEnumerable for vs foreach

C# IEnumerable for vs foreach,c#,for-loop,foreach,yield,C#,For Loop,Foreach,Yield,我有一个返回值的方法。e、 g: public static IEnumerable<int> GetValues() { for (int i = 0; i < 10; i++) { yield return i; } } 当我用for循环调用它时,产生返回i被称为阶乘10次 for (int i = 0;i< 10;i++) { Console.WriteLine(GetValues().ElementAt(i));

我有一个返回值的方法。e、 g:

public static IEnumerable<int> GetValues()
{
    for (int i = 0; i < 10; i++)
    {
        yield return i;
    }
}
当我用for循环调用它时,
产生返回i被称为阶乘10次

for (int i = 0;i< 10;i++)
{
    Console.WriteLine(GetValues().ElementAt(i));
}

也不起作用。

一个
IEnumerable
没有索引器。如果您以这种方式使用for循环,它将对每个
i
IEnumerable
上进行迭代


一个好的解决方案是foreach循环。如果需要索引,可以使用计数器对每个循环迭代进行计数。

如果要按索引访问项并减少
GetValues
调用,则必须具体化由
GetValues
生成的惰性枚举:

var values = GetValues()
    .ToArray();

for (int i = 0; i < values.Length; i++)
{
    Console.WriteLine(values[i]);
}

将一次又一次地创建一个新的惰性枚举数。

如果返回一个
IList
(例如
int[]
List
),则
可枚举。ElementAt
使用索引器而不是枚举所有

public static IList<int> GetValues()
{
    int[] ints = Enumerable.Range(0, 10).ToArray();
    return ints;
}
公共静态IList GetValues()
{
int[]ints=Enumerable.Range(0,10).ToArray();
返回整数;
}
但这并不是使用延迟执行,所以您要将所有内容加载到内存中


以下是。

您不能向后移动或引用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中的元素数)

您可以创建自己的代理、惰性类,该类仅在需要时枚举枚举器

    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();
    }

您是否总是遍历整个集合?
收益率返回i
不会被称为“阶乘10”次-它只是O(n^2)。现在还不清楚您在这里想要实现什么-为什么不直接使用foreach循环呢?如果您能给我们更多的动力,我们将更有可能帮助您。唯一的方法是返回一个
IList
,然后
ElementAt
使用索引器,而不是枚举所有内容。@fubo:但是为了理解最合适的解决方案,我们需要知道您为什么不想使用foreach,考虑到这是您给出的代码中最明显的解决方案。特别是,对于未来的其他人来说,这个问题并不像你没有表达为什么最明显的解决方案不适合你那样有用。那么,我认为它实际上并不是非常有用——它感觉像是一个问题“当然,我可以走直路回家——但如果我不想因为任何具体原因而特意走,那么最好的路线是什么?“您接受的解决方案是懒惰的,但会记住它已经产生的所有值-因此,对于您只需要按顺序排列项目的情况,例如,它不是最合适的解决方案。您可以有一个
for
循环,它仍然使用
MoveNext
而不是索引。。。但我认为这不能满足您的问题。如果函数返回
IList
,则签名不应
IEnumerable
@Magnus:changed,尽管这是一个偏好问题。我想说明的是,如果方法返回
IList
IEnumerable
,对于
ElementAt
来说并不重要,但真正返回的内容却很重要。也许OP需要一个
IEnumerable
。@TimSchmelter-这种方法也让我失去了延迟加载的优势。@fubo:正如我在回答中提到的那样,
.ToList()
也是我的第一种方法,但它也失去了延迟加载的优势。我可以
Console.WriteLine()
第一个元素,即使最后一个元素没有返回
IEnumerator
也是
IDisposable
,因此它将使用(var enumerator=…){for(;enumerator.MoveNext();)…}
转换为
。在枚举文件行、数据库记录等时,这种行为非常适合C#(与Java相比)。
foreach (var value in GetValues())
{
    Console.WriteLine(value);
}
using (var enumerator = GetValues().GetEnumerator())
{
    while(enumerator.MoveNext())
    {
        var value = enumerator.Current;
        Console.WriteLine(value);
    }
}
    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