Search code examples
c#ienumerableienumerator

C#: Circular enumeration of IEnumerable<T>


There is the command hierarchy in my current application.

public interface ICommand
{
    void Execute();
}

So, some commands are stateful, some are not.

I need to enumerate IEnumerable in the circular way for some command implementation during command execution.

public class GetNumberCommand : ICommand
{
    public GetNumberCommand()
    {
        List<int> numbers = new List<int>
            {
                1, 2, 3
            };
    }
    
    public void Execute()
    {
        // Circular iteration here.
        // 1 => 2 => 3 => 1 => 2 => 3 => ...
    }

    public void Stop()
    {
        // Log current value. (2 for example)
    }
}

Execute is called from time to time, so it is necessary to store the iteration state.

How to implement that circular enumeration?

I have found two solutions:

  1. Using the IEnumerator<T> interface. It looks like:

     if (!_enumerator.MoveNext())
     {
         _enumerator.Reset();
         _enumerator.MoveNext();
     }
    
  2. Using the circular IEnumerable<T> (yield forever the same sequence): “Implementing A Circular Iterator” - HonestIllusion.Com.

Maybe, there are more ways to achieve it.
What would you recommend to use and why?


Solution

  • You can use this extension method:

    public static IEnumerable<T> Cyclic<T>(this IEnumerable<T> @this)
    {
        while (true)
            foreach (var x in @this)
                yield return x;
    }
    

    In that way:

    public class GetNumberCommand : ICommand
    {
        private readonly IEnumerator<int> _commandState = new[] { 1, 2, 3 }.Cyclic().GetEnumerator();
    
        public void Execute()
        {
            _commandState.MoveNext();
            var state = _commandState.Current;
            
            //
            // Do stuff with state
            //
        }
    
        public void Stop()
        {
            var state = _commandState.Current;                
            // Log state value. (2 for example)
        }
    }