理想的c# IEnumerable通用数字序列的开始和间隔

本文关键字:开始 字序 数字 IEnumerable 理想 | 更新日期: 2023-09-27 18:13:24

我一直在寻找,但找不到一个实现IEnumerable的通用类的惯用示例,它这样做:构造函数接受startinterval, GetEnumerator返回IEnumerator,它从start开始,一直返回具有间隔的项目。

换句话说,就像:

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

有很多相关的问题和小片段,但我还没有找到一个完整类的好例子,它至少做了一些类似的事情。这是关于如何实现这个,所以如果有一个现有的类做这个,这将是很好的知道,但不是一个问题的答案。

所以,实际的问题是:哪一个最新的c#版本引入了有用的新功能,用它来完成这个任务的理想示例代码是什么?

如果没有经验的c#开发人员在使用这种类时会犯一些常见的陷阱和错误,那么最好知道这些。


更新:因为我要求的确切的事情似乎是不可能的,我想下一个最好的事情是用lambda代替interval来获得下一个项目,或类似的东西。

理想的c# IEnumerable通用数字序列的开始和间隔

感谢dlev的建议Func,我最初只是使用一个助手类。

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

你可以这样使用它:

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

或者,您可以要求T实现某种Addable接口并调用Add方法,但我认为这更清晰。

您所描述的与MoreLinq的Generate函数非常相似。

实现很简单:

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

作为使用委托的替代方法,您可以使用MiscUtil中的泛型操作符。使用它,您的代码看起来像这样:

IEnumerator<T> GetEnumerator() 
{
    for(T n = start ; ; n = Operator.Add(n, interval))
        yield return n; 
}