Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: circular enumeration of IEnumerable

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?

like image 609
Sergey Vyacheslavovich Brunov Avatar asked May 22 '12 05:05

Sergey Vyacheslavovich Brunov


3 Answers

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)
    }
}
like image 185
Federico Alterio Avatar answered Sep 19 '22 03:09

Federico Alterio


Instead of dealing with IEnumerator interface,

foreach (var x in GetSomething())
{
     if (someCondition) break;
}



public IEnumerable<int> GetSomething()
{
    List<int> list = new List<int>() { 1, 2, 3 };
    int index=0;

    while (true)
        yield return list[index++ % list.Count];
}
like image 6
L.B Avatar answered Oct 15 '22 09:10

L.B


Here's one I just implemented as an extension.

using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace DroopyExtensions
{
    public static class CircularEnumaratorExtensionMethod
    {

        public static IEnumerator<T> GetCircularEnumerator<T>(this IEnumerable<T> t) 
        {
            return new CircularEnumarator<T>(t.GetEnumerator());
        }

        private class CircularEnumarator<T> : IEnumerator<T>
        {
            private readonly IEnumerator _wrapedEnumerator;

            public CircularEnumarator(IEnumerator wrapedEnumerator)
            {
                this._wrapedEnumerator = wrapedEnumerator;
            }

            public object Current => _wrapedEnumerator.Current;

            T IEnumerator<T>.Current =>  (T)Current;

            public void Dispose()
            {

            }

            public bool MoveNext()
            {
                if (!_wrapedEnumerator.MoveNext())
                {
                    _wrapedEnumerator.Reset();
                    return _wrapedEnumerator.MoveNext();
                }
                return true;
            }

            public void Reset()
            {
                _wrapedEnumerator.Reset();
            }
        }
    }   
} 

To use it, all you have to do is

using DroopyExtensions;

class Program
{
    static void Main(string[] args)
    {
        var data = new List<string>() {"One", "Two", "Tree"};
        var dataEnumerator = data.GetCircularEnumerator();
        while(dataEnumerator.MoveNext())
        {
            Console.WriteLine(dataEnumerator.Current);
        }
    }
}
like image 4
Ivan Maia Avatar answered Oct 15 '22 09:10

Ivan Maia