在foreach循环内手动增加枚举数

时间:2012-02-13 01:01:59

标签: c# loops foreach ienumerable ienumerator

我在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的最佳方法是什么?

编辑: 编辑显示有一个条件满足后我想要执行的w​​hile循环之后的代码这就是为什么我想在内部手动递增然后突破它而不是继续这会让我回到顶部。如果这是不可能的,我相信最好的办法是重新设计我是如何做到的。

8 个答案:

答案 0 :(得分:12)

许多其他答案建议使用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循环如何推进呢?

在你的循环中,timeDateTime。不需要在循环中实现接口或模式的对象。 times是一系列DateTime值,它是必须实现可枚举模式的值。这通常通过实施IEnumerable<T>IEnumerable接口来实现,这些接口只需要T GetEnumerator()object GetEnumerator()方法。这些方法返回一个实现IEnumerator<T>IEnumerator的对象,它定义了bool MoveNext()方法和Tobject Current属性。但是time无法转换为IEnumerator,因为它不是这样的,times序列也不是。

答案 1 :(得分: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   
     } 
}

这相当于您的伪代码,因为对于条件为false的每个项目,将执行在满足条件后执行代码的部分

我在所有这些方面的假设是评估列表中每个项目的条件。

答案 2 :(得分:3)

也许您可以使用continue

答案 3 :(得分:3)

您将使用continue语句:  continue;

答案 4 :(得分: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();

这是你尝试做的事情吗?

答案 5 :(得分: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; }
        }

    }
}

答案 6 :(得分: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());

答案 7 :(得分:0)

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

struct ControllableEnumeratorItem<T>
{
  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”方法)。