Sequence generators not only for potatoes but also for apples and oranges The last question about sequence generators Growing potatoes in delayed sequences was only about potatoes. I thougt why not make it work with apples and oranges too so I made it generic.



The base class got a new name and a generic argument and now looks like this:

public abstract class GeneratedSequence&lt;T&gt; : IEnumerable&lt;T&gt;
{
    protected GeneratedSequence(int count) { Count = count; }

    public int Count { get; }

    public IEnumerator&lt;T&gt; GetEnumerator() =&gt; Generate().Take(Count).GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() =&gt; GetEnumerator();

    protected abstract IEnumerable&lt;T&gt; Generate();
}




The RegularSequence has been upgraded and is now generic too:

public class RegularSequence&lt;T&gt; : GeneratedSequence&lt;T&gt;
{
    private readonly T _value;
    public RegularSequence(T value, int count) : base(count) { _value = value; }
    protected override IEnumerable&lt;T&gt; Generate()
    {
        while (true) yield return _value;
    }
}




The biggest change undergone however the FibonnaciSequence. Generics don't support arithmetic operations so I needed to add a lambda for the sum. Now it looks like this:

public class FibonacciSequence&lt;T&gt; : GeneratedSequence&lt;T&gt;
{
    private T _preview;
    private T _current;
    private readonly Func&lt;T, T, T&gt; _sum;

    public FibonacciSequence(T firstTwo, T firstStep, int count, Func&lt;T, T, T&gt; sum) : base(count)
    {
        _sum = sum;
        _preview = firstTwo;
        _current = _sum(_preview, firstStep);
    }   

    protected override IEnumerable&lt;T&gt; Generate()
    {
        yield return _preview;
        yield return _preview;
        yield return _current;

        while (true)
        {
            var newCurrent = _sum(_preview, _current);
            yield return newCurrent;
            _preview = _current;
            _current = newCurrent;
        }
    }
}




To simplify the creation process I also added a new FibonacciSequenceFactory:

public class FibonacciSequenceFactory
{
    public static FibonacciSequence&lt;TimeSpan&gt; Create(TimeSpan firstTwo,TimeSpan firstStep, int count)
    {
        return new FibonacciSequence&lt;TimeSpan&gt;(firstTwo, firstStep, count, (x, y) =&gt; x + y);
    }

    public static FibonacciSequence&lt;int&gt; Create(int firstTwo, int firstStep, int count)
    {
        return new FibonacciSequence&lt;int&gt;(firstTwo, firstStep, count, (x, y) =&gt; x + y);
    }
}




I've applied the same pattern to the GeometricSequence but as it already had a lambda for the incrementation I just changed the argument to T:

public class GeometricSequence&lt;T&gt; : GeneratedSequence&lt;T&gt;
{
    private T _current;
    private readonly Func&lt;T, T&gt; _increment;
    public GeometricSequence(T first, Func&lt;T, T&gt; increment, int count) : base(count)
    {
        _current = first;
        _increment = increment;
    }
    protected override IEnumerable&lt;T&gt; Generate()
    {
        yield return _current;

        while (true)
        {
            yield return (_current = _increment(_current));
        };
    }
}

public class GeometricSequenceFactory
{
    public static GeometricSequence&lt;TimeSpan&gt; Double(TimeSpan first, int count)
    {
        return new GeometricSequence&lt;TimeSpan&gt;(first, x =&gt; TimeSpan.FromTicks(x.Ticks * 2), count);
    }

    public static GeometricSequence&lt;TimeSpan&gt; Triple(TimeSpan first, int count)
    {
        return new GeometricSequence&lt;TimeSpan&gt;(first, x =&gt; TimeSpan.FromTicks(x.Ticks * 3), count);
    }

    public static GeometricSequence&lt;TimeSpan&gt; Halve(TimeSpan first, int count)
    {
        return new GeometricSequence&lt;TimeSpan&gt;(first, x =&gt; TimeSpan.FromTicks(x.Ticks / 2), count);
    }
}




Usage example:

var fs = FibonacciSequenceFactory.Create(2, 4, 10);
A slightly different approach is to avoid the sublclassing and instead feed the generic DelaySequence class with a generator function. It makes it more flexible.

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

namespace CR146856
{
  public class MyDelaySequence&lt;T&gt; : IEnumerable&lt;T&gt;
  {
    Func&lt;IEnumerable&lt;T&gt;&gt; m_generator;
    int m_count = 0;

    /// &lt;summary&gt;
    /// A contructor for a plain sequence of regular delays
    /// &lt;/summary&gt;
    /// &lt;param name="count"&gt;Number of retries.&lt;/param&gt;
    /// &lt;param name="delay"&gt;The delay after each retry.&lt;/param&gt;
    public MyDelaySequence(int count, T delay)
    {
      m_count = count;
      m_generator = () =&gt;
      {
        return Enumerable.Range(1, count).Select(i =&gt; delay);
      };
    }

    /// &lt;summary&gt;
    /// Constructor for a custom delay generator.
    /// &lt;/summary&gt;
    /// &lt;param name="count"&gt;Number of retries.&lt;/param&gt;
    /// &lt;param name="generator"&gt;A custom generator of delays between retries.&lt;/param&gt;
    public MyDelaySequence(int count, Func&lt;IEnumerable&lt;T&gt;&gt; generator)
    {
      m_count = count;
      m_generator = generator;
    }

    public IEnumerator&lt;T&gt; GetEnumerator()
    {
      return m_generator().Take(m_count).GetEnumerator();
    }

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

  class Program
  {
    static IEnumerable&lt;int&gt; GeometricDelayGenerator()
    {
      int delay = 1000;
      while (true)
      {
        yield return delay;
        delay += delay;
      }
    }

    static IEnumerable&lt;int&gt; FibonacciIntDelayGenerator()
    {
      int first = 1000;

      yield return first;
      yield return first;

      int second = first + 3000;
      yield return second;


      while (true)
      {
        yield return first + second;
        int tmp = first;
        first = second;
        second = first + tmp;
      }
    }

    static IEnumerable&lt;TimeSpan&gt; FibonacciTimeSpanDelayGenerator()
    {
      TimeSpan first = TimeSpan.FromSeconds(1);

      yield return first;
      yield return first;

      TimeSpan second = first + TimeSpan.FromSeconds(3);
      yield return second;


      while (true)
      {
        yield return first + second;
        TimeSpan tmp = first;
        first = second;
        second = first + tmp;
      }
    }

    static void Main(string[] args)
    {
      try
      {
        TimeSpan seqDelay = TimeSpan.FromSeconds(1);
        int count = 7;

        foreach (var ts in new MyDelaySequence&lt;TimeSpan&gt;(count, FibonacciTimeSpanDelayGenerator))
        {
          Console.WriteLine(ts);
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"ERROR: {ex.Message}");
      }

      Console.WriteLine("END");
      Console.ReadLine();
    }
  }
}


That said I tend to think you overdo a rather simple construct:

    foreach (var delay in FibonacciIntDelayGenerator().Take(count))
    {
      Console.WriteLine($"If service not responding wait in {delay} ms and retry.");
    }


EDIT:

In case of a reusable library of functions I would do it the static way:

  public static class MyDelayGenerators
  {
    public static IEnumerable&lt;T&gt; DelayGenerator&lt;T&gt;(int retries, T initialDelay, Func&lt;T, T&gt; offsetFunc)
    {
      T delay = initialDelay;
      for (int i = 0; i &lt; retries; i++)
      {
        yield return delay;
        delay = offsetFunc(delay);
      }
    }

    public static IEnumerable&lt;T&gt; GeometricDelayGenerator&lt;T&gt;(int retries, T initialDelay, Func&lt;T, T&gt; sum)
    {
      return DelayGenerator(retries, initialDelay, sum);
    }

    public static IEnumerable&lt;int&gt; GeometricDelayGenerator(int retries, int initialDelay)
    {
      return DelayGenerator(retries, initialDelay, (d) =&gt; d + d);
    }

    public static IEnumerable&lt;TimeSpan&gt; GeometricDelayGenerator(int retries, TimeSpan initialDelay)
    {
      return DelayGenerator(retries, initialDelay, (d) =&gt; d + d);
    }

    public static IEnumerable&lt;int&gt; RegularDelayGenerator(int retries, int delay)
    {
      for (int i = 0; i &lt; retries; i++)
      {
        yield return delay;
      }
    }

    public static IEnumerable&lt;TimeSpan&gt; RegularDelayGenerator(int retries, TimeSpan delay)
    {
      for (int i = 0; i &lt; retries; i++)
      {
        yield return delay;
      }
    }

    public static IEnumerable&lt;T&gt; FibonacciDelayGenerator&lt;T&gt;(int retries, T first, T firstOffset, Func&lt;T, T, T&gt; sum)
    {
      yield return first;
      yield return first;

      T second = sum(first, firstOffset);
      yield return second;

      retries -= 3;
      for (int i = 0; i &lt; retries; i++)
      {
        T tmp = sum(first, second);
        yield return tmp;
        first = second;
        second = tmp;
      }
    }

    public static IEnumerable&lt;int&gt; FibonacciDelayGenerator(int retries, int first = 1000, int firstOffset = 1000)
    {
      return FibonacciDelayGenerator(retries, first, firstOffset, (f, s) =&gt;
      {
        int tmp = f;
        f = s;
        s = f + tmp;
        return s;
      });
    }

    public static IEnumerable&lt;TimeSpan&gt; FibonacciDelayGenerator(int retries, TimeSpan first, TimeSpan firstOffset)
    {
      return FibonacciDelayGenerator(retries, first, firstOffset, (f, s) =&gt;
      {
        TimeSpan tmp = f;
        f = s;
        s = f + tmp;
        return s;
      });
    }
  }


It has a number of predefined functions and a set of generic as well to be use on the fly.