Manually increment an enumerator inside foreach loop

前端 未结 8 1050
情话喂你
情话喂你 2021-01-18 06:20

I have a nested while loop inside a foreach loop where I would like to advance the enumerator indefinitately while a certain condition is met. To do this I try casting the e

相关标签:
8条回答
  • 2021-01-18 06:49

    I definitely do not condone what I am about to suggest, but you can create a wrapper around the original IEnumerable to transform it into something that returns items which can be used to navigate the underlying the enumerator. The end result might look like the following.

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

    Then we need to create our StepWise extension method.

    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; }
            }
    
        }
    }
    
    0 讨论(0)
  • 2021-01-18 06:53

    Perhaps you can use continue?

    0 讨论(0)
  • 2021-01-18 06:54

    You could use a func as your iterator and keep the state that you are changing in that delegate to be evaluated each iteration.

    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());
    
    0 讨论(0)
  • 2021-01-18 06:55

    You would use the continue statement: continue;

    0 讨论(0)
  • 2021-01-18 07:02

    This is just a guess, but it sounds like what you're trying to do is take a list of datetimes and move past all of them which meet a certain criteria, then perform an action on the rest of the list. If that's what you're trying to do, you probably want something like SkipWhile() from System.Linq. For example, the following code takes a series of datetimes and skips past all of them which are before the cutoff date; then it prints out the remaining datetimes:

    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();
    

    Is that the sort of thing you're trying to do?

    0 讨论(0)
  • 2021-01-18 07:04

    You cannot modify the enumerator from inside the for loop. The language does not permit this. You need to use the continue statement in order to advance to the next iteration of a loop.

    However, I'm not convinced that your loop even needs a continue. Read on.

    In the context of your code you would need to convert the while to an if in order to make the continue refer to the foreach block.

    foreach (DateTime time in times)       
    {    
         if (condition) 
         {
                 // perform action
                 continue;
         }
         // code to execute if condition is not met    
    }
    

    But written like this it is clear that the following equivalent variant is simpler still

    foreach (DateTime time in times)       
    {    
         if (condition) 
         {
                 // perform action
         }
         else
         {
                // code to execute if condition is not met   
         } 
    }
    

    This is equivalent to your pseudo-code because the part marked code to execute after while condition is met is executed for each item for which condition is false.

    My assumption in all of this is that condition is evaluated for each item in the list.

    0 讨论(0)
提交回复
热议问题