Question

I was looking for, but could not find, an idiomatic example for a generic class implementing IEnumerable, which does this: constructor takes start and interval, and GetEnumerator returns IEnumerator, which starts from starts and goes on forever returning items with the interval.

In other words, something like:

public class Sequence<T> : IEnumerable<T> where T :... {

    public T start { get; private set; }
    public T interval { get; private set; }

    Sequence(T start, T interval) 
    {
        ...
    }

    IEnumerator<T> GetEnumerator() 
    {
        for(T n = start ; ; n += interval) // does not compile
            yield return n; 
    }

    ... what else?
}

There are lots of related questions, and small snippets, bits&pieces, but I have not been able to find a single nice example of a complete class, which does at least something similar enough. This is about how to implement this, so if there is existing class which does just this, it'd be good to know, but not an answer to the question.

So, actual question(s): Which is the most recent C# version that has introduced new features useful for this, and what is the ideal example code to do this with it?

Also if there are any common pitfalls, mistakes which inexperienced C# developers make related to this kind of a class, those would be good to know.


Update: since the exact thing I'm asking for seems impossible, I guess the next best thing is replacing interval with a lambda for getting the next item, or something like that.

Was it helpful?

Solution

Thanks to dlev for suggesting Func, I was originally just using a helper class.

public class Sequence<T> : IEnumerable<T>
{
  public T Start { get; private set; }
  public T Interval { get; private set; }
  private Func<T, T, T> Adder { get; set; }

  public Sequence(T start, T interval, Func<T,T,T> adder)
  {
    Start = start;
    Interval = interval;
    Adder = adder;
  }

  public IEnumerator<T> GetEnumerator()
  {
    for (T n = Start; ; n = Adder.Invoke(n, Interval))
      yield return n;
  }

  IEnumerator IEnumerable.GetEnumerator()
  {
    return this.GetEnumerator();
  }
}

You can then use it like this:

int i = 0;
foreach (int t in new Sequence<int>(3, 4, (int a, int b) => a + b))
{
    if (i == 10)
    {
        break;
    }
    i++;
    Console.WriteLine(t);
}

Alternatively, you could require that T implement some sort of Addable interface and call that Add method, but I think this is cleaner all around.

OTHER TIPS

What you're describing is very similar to the Generate function of MoreLinq.

The implementation is simple enough:

public static IEnumerable<TResult> Generate<TResult>(TResult initial, Func<TResult, TResult> generator)
{
    if (generator == null) throw new ArgumentNullException("generator");
    return GenerateImpl(initial, generator);
}

private static IEnumerable<TResult> GenerateImpl<TResult>(TResult initial, Func<TResult, TResult> generator)
{
    TResult current = initial;
    while (true)
    {
        yield return current;
        current = generator(current);
    }
}

As an alternative to using delegates, you could use generic operators from MiscUtil. Using that, your code would look like this:

IEnumerator<T> GetEnumerator() 
{
    for(T n = start ; ; n = Operator.Add(n, interval))
        yield return n; 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top