我知道我可以使用for
语句来达到相同的效果,但是在C#中我能通过反向循环foreach
循环吗?
如果你使用的是 .NET 3.5,你可以这样做:
IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())
这种方法效率不高,因为它需要正向遍历枚举器,将所有内容放入堆栈中,然后以相反的顺序弹出所有内容。
如果您有一个可以直接索引的集合(例如 IList),则应该使用 for
循环。
如果您使用的是 .NET 2.0 并且无法使用 for 循环(即只有 IEnumerable),那么您就必须编写自己的 Reverse 函数。代码如下:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
这种方法依赖于一些可能不太明显的行为。当您将IEnumerable传递给stack构造函数时,它会遍历它并将项目推入堆栈。当您遍历堆栈时,它会以相反的顺序弹出内容。
如果将一个永远不停止返回项目的IEnumerable传递给它,则此方法和.NET 3.5的 Reverse()
扩展方法显然会崩溃。
当使用列表(直接索引)时,不能像使用for
循环那样高效。
编辑:通常情况下,当您能够使用for
循环时,它可能是正确的方法。此外,尽管foreach
按顺序实现,但该结构本身是用于表达独立于元素索引和迭代顺序的循环的,这在并行编程中特别重要。我认为依赖顺序进行迭代的循环不应使用foreach
。
像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";
}
}
你如果尝试反向迭代,你会期待什么结果?在使用 foreach
进行迭代之前,通过 reverse
方法反转列表:
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
myList
的说明会很有帮助。IEnumerable.Reverse在这里行不通。 - nawfalmyList
似乎是类型为 System.Collections.Generic.List<System.Windows.Documents.List>
(或任何其他自定义的 List
类型),否则这段代码就无法工作 :P - Martin SchneiderList<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
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"
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
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 IEnumerator<TObject> GetEnumerator()
{
for (var i = items.Count - 1; i >= 0; i--)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
当我们将List.Reverse()方法和foreach结合使用时,这变得非常简单。
List<int> numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
// Reverse the order of the elements
foreach (int i in numbers.Reverse())
{
// Display the element
Console.WriteLine(i);
}
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
如何反转它? - Suncat2000