我知道我可以使用一条for
语句并达到相同的效果,但是我可以foreach
在C#中通过一个循环向后循环吗?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
您将如何逆转?
我知道我可以使用一条for
语句并达到相同的效果,但是我可以foreach
在C#中通过一个循环向后循环吗?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
您将如何逆转?
Answers:
使用列表(直接索引)时,您无法像使用for
循环那样高效地完成它。
编辑:这通常意味着,当您能够使用for
循环时,它可能是此任务的正确方法。另外,就foreach
顺序实现而言,构造本身是为了表示独立于元素索引和迭代顺序的循环而构建的,这在并行编程中尤其重要。这是我的观点是迭代依托为了不应该使用foreach
的循环。
yield return
”来实现。
如果您使用的是.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传递给堆栈构造函数时,它将迭代它,并将项目推入堆栈。然后,当您遍历堆栈时,它将以相反的顺序弹出。
Reverse()
如果为它提供一个IEnumerable且永不停止返回项目,则该方法和.NET 3.5 扩展方法显然会崩溃。
正如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在这里不起作用。
myList
似乎是类型System.Collections.Generic.List<System.Windows.Documents.List>
(或任何其他自定义List
类型),否则此代码将不起作用:P
有时,您可能没有足够的索引编制能力,或者您想要撤消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);
}
用法:
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/zh-CN/library/vstudio/bb358497(v=vs.100).aspx
如果可以更改实现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更好)。
在乔恩·斯凯特(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)) {
.
.
}
我已经使用了这个有效的代码
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) {
.Reverse()
实际上是在修改列表。
这个效果很好
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]);
}