Manually incrementing an enumerator inside a foreach loop

I have a closed while loop inside the foreach loop, where I would like to increase the counter indefinitely while a certain condition is fulfilled. To do this, I try to list the enumerator in IEnumerator <T> (which should be if it is in the foreach loop), and then calls MoveNext () on the custom object, but it gives me an error saying that I cannot convert it.

It is not possible to convert the type "System.DateTime" to System.Collections.Generic.IEnumerator through a link conversion, box conversion, unbox conversion, conversion conversion, or conversion of the null type.

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 } 

What is the best way to manually increment IEnumerator inside a foreach loop?

EDIT: Edited to show if there is code after a while loop that I would like to execute after the condition is fulfilled, so I wanted to manually increment it inside and then exit it, and not continue, which will bring me back up. If this is not possible, I find it best to redesign how I do it.

+6
source share
8 answers

Many other answers recommend using continue , which can really help you do what you need. However, in the interest of displaying the manual movement of the counter, you must first have an enumerator, which means that you write your loop as 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 } } 

From your comments:

How can a foreach loop promote it if it does not implement the IEnumerator interface?

There is a DateTime in your time loop. This is not an object that must implement an interface or template to work in a loop. times is a sequence of DateTime values, this is the one that an enumerated template should implement. This is usually done using the IEnumerable<T> and IEnumerable interfaces, which simply require the T GetEnumerator() and object GetEnumerator() methods. The methods return an object that implements IEnumerator<T> and IEnumerator , which define the bool MoveNext() method and the T property or object Current . But time cannot be attributed to IEnumerator , because it is not such a thing, and is not a sequence of times .

+12
source

You cannot change an enumerator from a for loop. Language does not allow this. You need to use the continue statement to advance to the next iteration of the loop.

However, I'm not sure your loop even needs to be continued. Read on.

In the context of your code, you will need to convert the time to if if you need to go to the foreach block to continue.

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

But it is written that it is clear that the next equivalent option is even simpler

 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 executable component that will be executed after the condition is satisfied is executed for each element for which the condition is false.

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

+5
source

Perhaps you can use continue ?

+3
source

You must use the continue statement: continue;

+3
source

This is just an assumption, but it looks like you are trying to make a time list and go past all of them that meet certain criteria, and then perform the action in the rest of the list. If this is what you are trying to do, you probably want something like SkipWhile () from System.Linq. For example, the following code takes a series of datetimes and skips all of them that are before the cutoff date; then it prints 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 this what you are trying to do?

+1
source

I definitely do not approve of what I'm going to offer, but you can create a wrapper around the original IEnumerable to convert it to something that returns elements that can be used to navigate the enumerator base. The end result may look like this.

 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 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; } } } } 
+1
source

You can use func as your iterator and save the state that you change in this doing for 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
source

One alternative that has not yet been mentioned is for the enumerator to return a wrapper object that allows access to itself in addition to the data item that is being enumerated. For sample:

  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;}
 }

This approach can also be used by data structures that want collections to be modified in a controlled manner during enumeration (for example, by including the DeleteCurrentItem, AddBeforeCurrentItem, and AddAfterCurrentItem methods).

0
source

Source: https://habr.com/ru/post/908288/


All Articles