可以通过foreach向后迭代?

JL.*_*JL. 117 c# foreach

我知道我可以使用一个for语句并实现相同的效果,但是我可以foreach在C#中循环回循环吗?

Mat*_*lls 137

如果您使用的是.NET 3.5,则可以执行以下操作:

IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())
Run Code Online (Sandbox Code Playgroud)

它不是非常有效,因为它必须基本上通过枚举器转发将所有内容放在堆栈上然后以相反的顺序将所有内容弹出.

如果你有一个可直接索引的集合(例如IList),你肯定应该使用for循环.

如果你使用的是.NET 2.0并且不能使用for循环(即你只有一个IEnumerable),那么你只需要编写自己的Reverse函数.这应该工作:

static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
    return new Stack<T>(input);
}
Run Code Online (Sandbox Code Playgroud)

这依赖于一些可能不那么明显的行为.当您将IEnumerable传递给堆栈构造函数时,它将遍历它并将项目推送到堆栈.然后当你遍历堆栈时,它会以相反的顺序弹出一些东西.

Reverse()如果你提供一个永远不会停止返回项目的IEnumerable,这个和.NET 3.5 扩展方法显然会爆炸.

  • 我错过了什么或你的.Net 3.5解决方案实际上没有用吗?Reverse()反转列表并且不返回它.太糟糕了,因为我希望有这样的解决方案. (11认同)
  • @ user12861,Micky Duncan:答案没错,你错过了什么.在System.Collections.Generic.List <T>上有一个Reverse方法,它执行就地反转.在.Net 3.5中,IEnumerable <T>上有一个名为Reverse的扩展方法.我已经将示例从var更改为IEnumerable <int>以使其更加明确. (6认同)
  • 有趣的.NET 2.0解决方案. (4认同)
  • 一些管理员将此答案标记为“错误”。Reverse()是void [1],因此上面的示例会导致编译错误。[1] http://msdn.microsoft.com/zh-cn/library/b0axc2h2(v=vs.110).aspx (2认同)

Sam*_*ell 78

使用列表(直接索引)时,不能像使用for循环那样有效.

编辑:通常意味着,当您能够使用for循环时,它可能是此任务的正确方法.另外,对于按foreach顺序实现的内容,构造本身是为了表达独立于元素索引和迭代顺序的循环而构建的,这在并行编程中尤为重要.这是我的观点是迭代依托为了不应该使用foreach的循环.

  • 我觉得你的最后一句话太笼统了。当然,在某些情况下,例如,某种 IEnumerable 需要按顺序迭代?在这种情况下你不会使用 foreach 吗?你会用什么? (5认同)
  • 我和@avl_sweden 的最初反应一样——“依赖顺序的迭代不应该使用 foreach”听起来过于宽泛。是的,foreach 非常适合表达与顺序无关的并行任务。但它也是现代[基于迭代器的编程](https://en.wikipedia.org/wiki/Iterator) 的重要组成部分。也许关键是如果有*两个不同的关键字*会更清晰/更安全,以澄清一个是否*断言*迭代是与顺序无关的?[给定像 Eiffel 这样可以传播合约的语言,对于给定的代码,这种断言可能是真还是假。] (3认同)
  • 更新:请参阅 [Bryan 对类似问题的回答 [(http://stackoverflow.com/a/3320924/199364),以获得更现代的答案,使用 Linq,并讨论两个列表和其他可枚举项。 (2认同)
  • 更新:[Jon Skeet](http://stackoverflow.com/a/1211626/199364) 有一个更优雅的答案,现在可以使用“`yield return`”。 (2认同)
  • foreach“是为表达独立于元素索引和迭代顺序的循环而构建”的想法是错误的。C#语言规范要求按顺序包含foreach过程元素。通过在迭代器上使用MoveNext或通过处理从零开始并在每次数组迭代中增加一的索引来处理。 (2认同)

Jon*_*eet 50

正如280Z28所说,IList<T>你可以使用索引.您可以在扩展方法中隐藏它:

public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
    for (int i = items.Count-1; i >= 0; i--)
    {
        yield return items[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

这将比Enumerable.Reverse()首先缓冲所有数据的速度更快.(我不相信Reverse有任何优化应用方式Count().)请注意,此缓冲意味着在您第一次开始迭代时完全读取数据,而在迭代时FastReverse将"看到"对列表所做的任何更改.(如果在迭代之间删除多个项目,它也会中断.)

对于一般序列,没有办法反向迭代 - 序列可能是无限的,例如:

public static IEnumerable<T> GetStringsOfIncreasingSize()
{
    string ret = "";
    while (true)
    {
        yield return ret;
        ret = ret + "x";
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你试图反过来迭代,你会发生什么?

  • >为什么用"> = 0"而不是"> -1"?因为> = 0更好地传达了人类阅读代码的意图.如果这样做会提高性能,编译器应该能够将其优化到等效值> -1. (14认同)
  • FastReverse(this IList&lt;T&gt; items) 应该是 FastReverse&lt;T&gt;(this IList&lt;T&gt; items.:) (2认同)
  • 分裂棍子 0&lt;=i 更好。多年来教了很多孩子数学,并帮助人们编码,我发现,如果人们总是将 a&gt;b 交换为 b&lt;a,那么就会大大减少人们犯的错误,因为事情会按照一行的自然顺序排列数字(或坐标系的 X 轴) - 唯一的缺点是如果您需要将方程式粘贴到 XML 中,例如 .config (2认同)

Pre*_*rem 11

在使用'foreach'进行迭代之前,请通过'reverse'方法反转列表:

    myList.Reverse();
    foreach( List listItem in myList)
    {
       Console.WriteLine(listItem);
    }
Run Code Online (Sandbox Code Playgroud)

  • 与[Matt Howells在'09的回答]相同(/sf/ask/84812591/#answer-1211610) (2认同)
  • @MA-Maddin没有两个是不同的.我想answerer依赖于List <T> .Reverse就位了. (2认同)

小智 5

如果您使用 List<T>,您还可以使用以下代码:

List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();
Run Code Online (Sandbox Code Playgroud)

这是一种将列表本身反向写入的方法。

现在foreach:

foreach(string s in list)
{
    Console.WriteLine(s);
}
Run Code Online (Sandbox Code Playgroud)

输出是:

3
2
1
Run Code Online (Sandbox Code Playgroud)


Vor*_*ire 5

有时,您可能没有足够的索引编制能力,或者您想要反转Linq查询的结果,或者您可能不想修改源集合,如果其中任何一个都是正确的,Linq可以为您提供帮助。

一种使用匿名类型和Linq Select来为Linq OrderByDescending提供排序键的Linq扩展方法。

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }
Run Code Online (Sandbox Code Playgroud)

用法:

    var eable = new[]{ "a", "b", "c" };

    foreach(var o in eable.Invert())
    {
        Console.WriteLine(o);
    }

    // "c", "b", "a"
Run Code Online (Sandbox Code Playgroud)

它被称为“反转”,因为它与“反向”同义,并且可以实现“列表反向”实现的歧义。

也可以反转集合的某些范围,因为Int32.MinValue和Int32.MaxValue不在任何种类的集合索引的范围之内,因此我们可以利用它们进行排序。如果元素索引低于给定范围,则将其分配给Int32.MaxValue,以便在使用OrderByDescending时其顺序不会改变,类似地,索引大于给定范围的元素将被分配给Int32.MinValue,以便它们在订购过程结束时出现。给定范围内的所有元素都分配了其正常索引,并相应地反转了。

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source, int index, int count)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i < index ? Int32.MaxValue : i >= index + count ? Int32.MinValue : i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }
Run Code Online (Sandbox Code Playgroud)

用法:

    var eable = new[]{ "a", "b", "c", "d" };

    foreach(var o in eable.Invert(1, 2))
    {
        Console.WriteLine(o);
    }

    // "a", "c", "b", "d"
Run Code Online (Sandbox Code Playgroud)

我不确定这些Linq实现是否会对性能产生影响,而不是使用临时List来包装要反转的集合。


在撰写本文时,我还不知道Linq自己的Reverse实现,但解决这个问题还是很有趣的。 https://msdn.microsoft.com/zh-CN/library/vstudio/bb358497(v=vs.100).aspx


Esk*_*ahn 5

稍微详细阐述乔恩·斯基特(Jon Skeet)的好答案,这可能是多用途的:

public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
    if (Forwards) foreach (T item in items) yield return item;
    else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}
Run Code Online (Sandbox Code Playgroud)

然后用作

foreach (var item in myList.Directional(forwardsCondition)) {
    .
    .
}
Run Code Online (Sandbox Code Playgroud)