在foreach循环内手动增加枚举数
我在foreach循环中有一个嵌套的while循环,我希望在满足某个条件时无限期地推进枚举器。 为此,我尝试将枚举器转换为IEnumerator (如果它在foreach循环中必须是这样)然后在已转换对象上调用MoveNext(),但它给出了一个错误,说我无法转换它。
无法通过引用转换,装箱转换,取消装箱转换,包装转换或空类型转换将类型“System.DateTime”转换为System.Collections.Generic.IEnumerator。
foreach (DateTime time in times) { while (condition) { // perform action // move to next item (time as IEnumerator).MoveNext(); // will not let me do this } // code to execute after while condition is met }
在foreach循环中手动增加IEnumerator的最佳方法是什么?
编辑:编辑显示有一个条件满足后我想要执行的while循环之后的代码这就是为什么我想在内部手动递增然后突破它而不是继续这会让我回到顶部。 如果这是不可能的,我相信最好的事情就是重新设计我是如何做到的。
许多其他答案建议使用continue
,这可能很好地帮助您做您需要做的事情。 但是,为了显示手动移动枚举器,首先必须使用枚举器,这意味着将循环写入一段while
。
using (var enumerator = times.GetEnumerator()) { DateTime time; while (enumerator.MoveNext()) { time = enumerator.Current; // pre-condition code while (condition) { if (enumerator.MoveNext()) { time = enumerator.Current; // condition code } else { condition = false; } } // post-condition code } }
从你的评论:
如果没有实现IEnumerator接口,foreach循环如何推进呢?
在循环中, time
是DateTime
。 不需要在循环中实现接口或模式的对象。 times
是一系列DateTime
值,它是必须实现可枚举模式的值。 这通常通过实现IEnumerable
和IEnumerable
接口来实现,这些接口只需要T GetEnumerator()
和object GetEnumerator()
方法。 这些方法返回一个实现IEnumerator
和IEnumerator
的对象,它定义了一个bool MoveNext()
方法和一个T
或object Current
属性。 但是time
不能转换为IEnumerator
,因为它不是这样的,也不是times
序列。
您无法从for循环内部修改枚举器。 该语言不允许这样做。 您需要使用continue语句才能进入循环的下一次迭代。
但是,我不相信你的循环甚至需要继续。 请继续阅读。
在代码的上下文中,您需要将while转换为if以使continue继续引用foreach块。
foreach (DateTime time in times) { if (condition) { // perform action continue; } // code to execute if condition is not met }
但是这样写的很清楚,以下等效变体仍然更简单
foreach (DateTime time in times) { if (condition) { // perform action } else { // code to execute if condition is not met } }
这相当于您的伪代码,因为对于条件为false的每个项, 将执行在满足条件之后执行的标记代码的部分。
我在所有这一切中的假设是评估列表中每个项目的条件。
也许你可以continue
使用?
您将使用continue语句: continue;
这只是一个猜测,但听起来你要做的就是获取一个日期时间列表并移过符合特定条件的所有日期,然后对列表的其余部分执行操作。 如果那是你想要做的,你可能想要像System.Linq这样的SkipWhile()。 例如,以下代码采用一系列日期时间并跳过截止日期之前的所有日期时间; 然后打印出剩余的日期时间:
var times = new List() { DateTime.Now.AddDays(1), DateTime.Now.AddDays(2), DateTime.Now.AddDays(3), DateTime.Now.AddDays(4) }; var cutoff = DateTime.Now.AddDays(2); var timesAfterCutoff = times.SkipWhile(datetime => datetime.CompareTo(cutoff) < 1) .Select(datetime => datetime); foreach (var dateTime in timesAfterCutoff) { Console.WriteLine(dateTime); } Console.ReadLine();
这是你想要做的事情吗?
我绝对不会宽恕我将要建议的内容,但你可以创建一个围绕原始IEnumerable
的包装器,将其转换为返回可用于导航枚举器底层的项目的东西。 最终结果可能如下所示。
public static void Main(string[] args) { IEnumerable times = GetTimes(); foreach (var step in times.StepWise()) { while (condition) { step.MoveNext(); } Console.WriteLine(step.Current); } }
然后我们需要创建StepWise
扩展方法。
public static class EnumerableExtension { public static IEnumerable> StepWise(this IEnumerable instance) { using (IEnumerator enumerator = instance.GetEnumerator()) { while (enumerator.MoveNext()) { yield return new Step (enumerator); } } } public struct Step { private IEnumerator enumerator; public Step(IEnumerator enumerator) { this.enumerator = enumerator; } public bool MoveNext() { return enumerator.MoveNext(); } public T Current { get { return enumerator.Current; } } } }
您可以使用func作为迭代器,并在每次迭代时保持您在该委托中更改的状态。
public static IEnumerable FunkyIEnumerable (this Func> nextOrNot) { while(true) { var result = nextOrNot(); if(result.Item1) yield return result.Item2; else break; } yield break; } Func> nextNumber = () => Tuple.Create(SomeRemoteService.CanIContinueToSendNumbers(), 1); foreach(var justGonnaBeOne in nextNumber.FunkyIEnumerable()) Console.Writeline(justGonnaBeOne.ToString());
还没有提到的一个替代方案是让枚举器返回一个包装器对象,除了枚举的数据元素之外,它还允许访问自身。 样品:
struct ControllableEnumeratorItem{ private ControllableEnumerator parent; public T Value {get {return parent.Value;}} public bool MoveNext(){return parent.MoveNext();} public ControllableEnumeratorItem(ControllableEnumerator newParent) {parent = newParent;} }
这种方法也可以被希望允许在枚举期间以受控方式修改集合的数据结构使用(例如,通过包括“DeleteCurrentItem”,“AddBeforeCurrentItem”和“AddAfterCurrentItem”方法)。