可以通过 foreach 向后迭代吗?

我知道我可以使用 for语句来达到同样的效果,但是我能在 C # 中反向循环 foreach循环吗?

225794 次浏览

如果你使用.NET 3.5,你可以这样做:

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

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

如果您有一个可直接索引的集合(例如 IList) ,那么您一定应该改用 for循环。

如果你在的话。NET 2.0,并且不能使用 for 循环(例如,你只有一个 IEnumable) ,那么你只需要编写你自己的反向函数。这应该会奏效:

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

这取决于一些或许并不那么明显的行为。当您向堆栈构造函数传递 IEnumable 时,它将遍历它并将项推送到堆栈上。然后,当您在堆栈中迭代时,它会以相反的顺序弹出一些东西。

这个和那个。NET 3.5 Reverse()扩展方法将明显爆炸,如果你给它一个 IEnumable 永远不会停止返回项目。

使用列表(直接索引)时,效率不如使用 for循环。

编辑: 这通常意味着,当你是 有能力使用 for循环时,它可能是这个任务的正确方法。另外,尽管 foreach是按顺序实现的,但是构造本身是为了表达独立于元素索引和迭代顺序的循环而构建的,这在 并行程序设计中尤其重要。依赖顺序的迭代不应该使用 foreach进行循环,这就是 我的意见

不。 ForEach 只是对每个项的集合进行迭代,顺序取决于它使用的是 IEnumable 还是 GetEnumerator ()。

正如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()更快。(我认为 Reverse没有像 Count()那样进行任何优化。)请注意,这种缓冲意味着在您第一次开始迭代时完全读取数据,而 FastReverse将在您迭代时“看到”对列表所做的任何更改。(如果在迭代之间删除多个项,它也会中断。)

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

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

如果您尝试反向迭代它,您希望发生什么?

效果不错

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

实现 IEnumable 或 IEnumable 的是 如果可以更改收集代码,则可以(例如您自己的 IList 实现)。

为您创建一个 迭代器来完成这项工作,例如,通过 数不胜数接口实现以下内容(假设“ item”是此示例中的 List 字段) :

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


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

因此,您的 List 将以相反的顺序遍历您的 List。

提示: 您应该在文档中清楚地说明列表的这种特殊行为(如果选择 Stack 或 Queue 这样自我解释的类名就更好了)。

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

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

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

现在是前锋:

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

输出结果是:

3
2
1

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

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

有时你没有索引的奢侈,或者你可能想逆转 Linq 查询的结果,或者你可能不想修改源集合,如果这些都是真的,Linq 可以帮助你。

使用匿名类型的 Linq 扩展方法与 LinqSelect 一起为 LinqOrderByDesending 提供排序键;

    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”,是因为它是“反向”的同义词,并且支持使用 List 反向实现消除歧义。

也可以反转一个集合的某些范围,因为 Int32.MinValue 和 Int32.MaxValue 超出了任何类型的集合索引的范围,我们可以利用它们来进行排序过程; 如果一个元素索引低于给定的范围,它就被赋予 Int32.MaxValue,这样在使用 OrderByDescent 时它的顺序不会改变,同样,索引大于给定范围的元素也会被赋予 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 自己的逆向实现,不过,解决这个问题还是很有趣的。 Https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx

我用了这个代码

                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) {

详细阐述了 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)) {
.
.
}

我在列表类型中添加了一个扩展,以帮助我反向懒惰地循环列表

public static IEnumerable<T> LazyReverse<T>(this IList<T> items)
{
for (var i = items.Count - 1; i >= 0; i--)
yield return items[i];
}

像这样使用

foreach(var item in list.LazyReverse()){
// do some work .....
if(item == somecondition){
break;// break without loading the reset of the list
}
}