可以通过foreach向后迭代?

时间:2009-07-31 09:39:35

标签: c# foreach

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

11 个答案:

答案 0 :(得分:130)

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

IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())

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

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

如果您使用的是.NET 2.0且无法使用for循环(即您只有IEnumerable),那么您只需编写自己的反向函数即可。这应该有效:

static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
    return new Stack<T>(input);
}

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

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

答案 1 :(得分:72)

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

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

答案 2 :(得分:48)

正如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];
    }
}

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

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

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

如果你试图反过来反复思考,你会发生什么?

答案 3 :(得分:8)

在使用&#39; foreach&#39;对于迭代,通过&#39;反向&#39;反转列表。方法:

    myList.Reverse();
    foreach( List listItem in myList)
    {
       Console.WriteLine(listItem);
    }

答案 4 :(得分:4)

如果您使用列表&lt; T&gt;,您还可以使用以下代码:

List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();

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

现在是foreach:

foreach(string s in list)
{
    Console.WriteLine(s);
}

输出结果为:

3
2
1

答案 5 :(得分:4)

有时你没有索引的奢侈,或者你想要反转Linq查询的结果,或者你可能不想修改源集合,如果其中任何一个都是真的,Linq可以帮助你。

使用带有Linq Select的匿名类型的Linq扩展方法为Linq OrderByDescending提供排序键;

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

用法:

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

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

    // "c", "b", "a"

它被命名为“Invert”,因为它与“Reverse”同义,并且可以通过List Reverse实现消除歧义。

也可以反转集合的某些范围,因为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);
    }

用法:

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

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

    // "a", "c", "b", "d"

我不确定这些Linq实现的性能命中率,而不是使用临时List来包装用于反转的集合。

在撰写本文时,我并不知道Linq自己的Reverse实现,但仍然很有趣。 https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx

答案 6 :(得分:2)

没有。 ForEach只是遍历每个项目的集合,顺序取决于它是使用IEnumerable还是GetEnumerator()。

答案 7 :(得分:2)

如果您可以更改实现IEnumerable或IEnumerable的集合代码(例如您自己的IList实现),那么是可能的。

创建一个Iterator为您完成这项工作,例如通过IEnumerable接口执行以下实现(假设'items'是此示例中的List字段):

public IEnumerator<TObject> GetEnumerator()
{
    for (var i = items.Count - 1; i >= 0; i--)
    { 
        yield return items[i];
    }
}

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

因此,您的列表将以相反的顺序在您的列表中迭代。

只是一个提示:你应该在文档中清楚地说明列表的这种特殊行为(甚至更好地选择自我解释的类名,如Stack或Queue)。

答案 8 :(得分:0)

详细说明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];
}

然后用作

foreach (var item in myList.Directional(forwardsCondition)) {
    .
    .
}

答案 9 :(得分:-1)

我使用了这个有效的代码

                if (element.HasAttributes) {

                    foreach(var attr in element.Attributes().Reverse())
                    {

                        if (depth > 1)
                        {
                            elements_upper_hierarchy_text = "";
                            foreach (var ancest  in element.Ancestors().Reverse())
                            {
                                elements_upper_hierarchy_text += ancest.Name + "_";
                            }// foreach(var ancest  in element.Ancestors())

                        }//if (depth > 1)
                        xml_taglist_report += " " + depth  + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + "   =   " + attr.Value + "\r\n";
                    }// foreach(var attr in element.Attributes().Reverse())

                }// if (element.HasAttributes) {

答案 10 :(得分:-8)

这很好用

List<string> list = new List<string>();

list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");

foreach (String s in list)
{
    Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}