7

我在 foreach 循环中有一个嵌套的 while 循环,我想在满足某个条件时无限期地推进枚举数。为此,我尝试将枚举器强制转换为 IEnumerator< T > (如果它在 foreach 循环中则必须如此),然后在强制转换的对象上调用 MoveNext() 但它给了我一个错误,说我无法转换它。

无法通过引用转换、装箱转换、拆箱转换、包装转换或空类型转换将类型“System.DateTime”转换为 System.Collections.Generic.IEnumerator。

        foreach (DateTime time in times)
        {
            while (condition)
            {
                // perform action
                // move to next item
                (time as IEnumerator<DateTime>).MoveNext(); // will not let me do this
            }

            // code to execute after while condition is met
         }

在 foreach 循环中手动增加 IEnumerator 的最佳方法是什么?

编辑:编辑显示while循环之后有代码,一旦满足条件,我想执行这就是为什么我想在while内手动递增然后打破它而不是继续,这会让我回到顶部. 如果这不可能,我相信最好的办法是重新设计我的工作方式。

4

8 回答 8

15

许多其他答案建议使用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<T>IEnumerable接口来实现,这些接口只需要T GetEnumerator()object GetEnumerator()方法。这些方法返回一个实现IEnumerator<T>and的对象IEnumerator,它定义了一个bool MoveNext()方法和一个Torobject Current属性。但time不能强制转换为IEnumerator,因为它不是这样的东西,times序列也不是。

于 2012-02-13T01:16:14.070 回答
5

您不能从 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   
     } 
}

这等效于您的伪代码,因为对于条件为假的每个项目,都会执行在满足条件后执行的部分标记代码。

我对所有这一切的假设是针对列表中的每个项目评估条件。

于 2012-02-13T01:05:00.850 回答
3

也许你可以使用continue

于 2012-02-13T01:06:03.757 回答
3

您将使用 continue 语句: continue;

于 2012-02-13T01:06:06.563 回答
1

这只是一个猜测,但听起来您正在尝试做的是获取日期时间列表并移过所有符合特定条件的日期时间,然后对列表的其余部分执行操作。如果这就是您想要做的,您可能需要 System.Linq 中的 SkipWhile() 之类的东西。例如,以下代码采用一系列日期时间并跳过所有在截止日期之前的日期时间;然后它打印出剩余的日期时间:

var times = new List<DateTime>()
    {
        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();

这是你想做的事情吗?

于 2012-02-13T01:19:47.913 回答
1

我绝对不会宽恕我将要提出的建议,但是您可以在原始文件周围创建一个包装器,IEnumerable将其转换为返回可用于导航底层枚举器的项目的东西。最终结果可能如下所示。

public static void Main(string[] args)
{
  IEnumerable<DateTime> times = GetTimes();
  foreach (var step in times.StepWise())
  {
    while (condition)
    { 
      step.MoveNext();
    }
    Console.WriteLine(step.Current);
  }
}

然后我们需要创建我们的StepWise扩展方法。

public static class EnumerableExtension
{
    public static IEnumerable<Step<T>> StepWise<T>(this IEnumerable<T> instance)
    {
        using (IEnumerator<T> enumerator = instance.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                yield return new Step<T>(enumerator);
            }
        }
    }

    public struct Step<T>
    {
        private IEnumerator<T> enumerator;

        public Step(IEnumerator<T> enumerator)
        {
            this.enumerator = enumerator;
        }

        public bool MoveNext()
        {
            return enumerator.MoveNext();
        }

        public T Current
        {
            get { return enumerator.Current; }
        }

    }
}
于 2012-02-16T15:13:52.417 回答
0

您可以使用 func 作为迭代器,并保持您在该委托中更改的状态以在每次迭代时进行评估。

public static IEnumerable<T> FunkyIEnumerable<T>(this Func<Tuple<bool, T>> nextOrNot)
    {
        while(true)
        {
           var result = nextOrNot();

            if(result.Item1)
                yield return result.Item2;

            else
                break;

        }

        yield break;

    }

    Func<Tuple<bool, int>> nextNumber = () => 
            Tuple.Create(SomeRemoteService.CanIContinueToSendNumbers(), 1);

    foreach(var justGonnaBeOne in nextNumber.FunkyIEnumerable())
            Console.Writeline(justGonnaBeOne.ToString());
于 2012-02-13T01:18:13.123 回答
0

尚未提及的一种替代方法是让枚举器返回一个包装器对象,该对象除了被枚举的数据元素外,还允许访问它自己。样品:

结构 ControllableEnumeratorItem<T>
{
  私有 ControllableEnumerator 父级;
  公共 T 值 {get {return parent.Value;}}
  public bool MoveNext() {return parent.MoveNext();}
  公共 ControllableEnumeratorItem(ControllableEnumerator newParent)
    {父母=新父母;}
}

这种方法也可以用于希望允许在枚举期间以受控方式修改集合的数据结构(例如,通过包括“DeleteCurrentItem”、“AddBeforeCurrentItem”和“AddAfterCurrentItem”方法)。

于 2012-02-16T14:21:33.287 回答