C# 如何使用流畅的界面构建序列?

C# 如何使用流畅的界面构建序列?,c#,.net,algorithm,linq,fluent-interface,C#,.net,Algorithm,Linq,Fluent Interface,我正在尝试使用fluent界面构建一个集合,类似于以下(简化)示例: 我能想到的最佳解决方案是add(): IEnumerable添加(此IEnumerable coll,T项) { foreach(var t in coll)收益率t; 收益回报项目; } 这似乎增加了很多开销,每次调用都会重复这些开销 有更好的办法吗 更新: 在匆忙中,我过度简化了示例,并遗漏了一个重要的需求。现有coll中的最后一项影响下一项。因此,一个稍微简化的示例: var a = StartWith(1).A

我正在尝试使用fluent界面构建一个集合,类似于以下(简化)示例:

我能想到的最佳解决方案是add():

IEnumerable添加(此IEnumerable coll,T项)
{
foreach(var t in coll)收益率t;
收益回报项目;
}
这似乎增加了很多开销,每次调用都会重复这些开销

有更好的办法吗

更新: 在匆忙中,我过度简化了示例,并遗漏了一个重要的需求。现有coll中的最后一项影响下一项。因此,一个稍微简化的示例:

   var a = StartWith(1).Add(2).Add(3).Add(4).ToArray();
   /* a = int[] {1,2,3,4};  */
   var a = StartWith(1).Times10Plus(2).Times10Plus(3).Times10Plus(4).ToArray();
   /* a = int[] {1,12,123,1234};  */

public static IEnumerable<T> StartWith<T>(T x)
{
    yield return x;
}

static public  IEnumerable<int> Times10Plus(this IEnumerable<int> coll, int item)
{
    int last = 0;
    foreach (var t in coll)
    {
        last = t;
        yield return t;
    }
    yield return last * 10 + item;
}
var a=StartWith(1).Times10Plus(2).Times10Plus(3).Times10Plus(4).ToArray();
/*a=int[]{1,121231234}*/
公共静态IEnumerable StartWith(TX)
{
收益率x;
}
静态公共IEnumerable Times10Plus(此IEnumerable coll,int项)
{
int last=0;
foreach(coll中的变量t)
{
last=t;
收益率t;
}
收益率返回最后一个*10+项目;
}

您可以执行以下操作:

public static class MySequenceExtensions
{
    public static IReadOnlyList<int> Times10Plus(
        this IReadOnlyList<int> sequence, 
        int value) => Add(sequence, 
                          value,
                          v => sequence[sequence.Count - 1] * 10 + v);

    public static IReadOnlyList<T> Starts<T>(this T first)
        => new MySequence<T>(first);

    public static IReadOnlyList<T> Add<T>(
        this IReadOnlyList<T> sequence,
        T item,
        Func<T, T> func)
    {
        var mySequence = sequence as MySequence<T> ?? 
                         new MySequence<T>(sequence);
        return mySequence.AddItem(item, func);
    }

    private class MySequence<T>: IReadOnlyList<T>
    {
        private readonly List<T> innerList;

        public MySequence(T item)
        {
            innerList = new List<T>();
            innerList.Add(item);
        }

        public MySequence(IEnumerable<T> items)
        {
            innerList = new List<T>(items);
        }

        public T this[int index] => innerList[index];
        public int Count => innerList.Count;

        public MySequence<T> AddItem(T item, Func<T, T> func)
        {
            Debug.Assert(innerList.Count > 0);
            innerList.Add(func(item));
            return this;
        }

        public IEnumerator<T> GetEnumerator() => innerList.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
}
var a = 1.Starts().Times10Plus(2).Times10Plus(3).Times10Plus(4).ToArray();
产生预期结果(
{1,12,123,1234}
),我认为性能合理。

您可以这样做:

public interface ISequence
{
    ISequenceOp StartWith(int i);
}

public interface ISequenceOp
{
    ISequenceOp Times10Plus(int i);
    int[] ToArray();
}

public class Sequence : ISequence
{
    public ISequenceOp StartWith(int i)
    {
        return new SequenceOp(i);
    }
}

public class SequenceOp : ISequenceOp
{
    public List<int> Sequence { get; set; }

    public SequenceOp(int startValue)
    {
        Sequence = new List<int> { startValue };
    }

    public ISequenceOp Times10Plus(int i)
    {
        Sequence.Add(Sequence.Last() * 10 + i);
        return this;
    }

    public int[] ToArray()
    {
        return Sequence.ToArray();
    }
}

这次聚会有点晚了,但这里有一些想法

首先,考虑更一般的问题:

public static IEnumerable<A> AggregateSequence<S, A>(
  this IEnumerable<S> items,
  A initial,
  Func<A, R, A> f) 
{
  A accumulator = initial;
  yield return accumulator;
  foreach(S item in items)
  {
    accumulator = f(accumulator, item);
    yield return accumulator;
  }
}
修改
IDeque
Deque
以明显的方式实现
IEnumerable
,然后免费获得
ToArray
。或者将其作为扩展方法:

static IEnumerable<T> EnumerateFromLeft(this IDeque<T> d)
{
  var c = d;
  while (!c.IsEmpty) 
  { 
    yield return c.PeekLeft(); 
    c = c.DequeueLeft(); 
  }
}
static IEnumerable Enumerated fromLeft(此标识为d)
{
var c=d;
而(!c.IsEmpty)
{ 
收益率返回c.peek left();
c=c.DequeueLeft();
}
}

有趣的问题。然而,我不确定有没有更好的方法来流利地完成这项工作。您可以让
Add
方法采用
params
数组,一次添加所有参数,但这并不能真正回答问题。我认为没有。除非您保留一些内部缓冲,例如使用
ImmutableList
或类似的方法。现在所有这些方法调用都只是将相同的引用返回到单个可变(和变异)数据结构。所以如果你写
var start=start(1);var nextValue=start.Times10Plus(2);控制台写入线(开始计数)它将打印2,而不是1,这是错误的。@servy是的,我知道,但从问题中不清楚这是否是不需要的。无论如何,这很容易修复,只需使用不可变队列作为内部类型即可。如果枚举不是性能关键的,您甚至可以使用一个简单、直接的、按相反顺序枚举的不可变堆栈。换句话说,您现在又开始使用OP的解决方案了。@servy no,在添加具有不可变堆栈的新元素时,访问最后一个元素实际上要快得多。一点也不一样。
public static IEnumerable<A> AggregateSequence<S, A>(
  this IEnumerable<S> items,
  A initial,
  Func<A, R, A> f) 
{
  A accumulator = initial;
  yield return accumulator;
  foreach(S item in items)
  {
    accumulator = f(accumulator, item);
    yield return accumulator;
  }
}
static IDeque<T> StartWith<T>(T t) => Deque<T>.Empty.EnqueueRight(t);
static IDeque<T> Op<T>(this IDeque<T> d, Func<T, T> f) => d.EnqueueRight(f(d.PeekRight()));
static IDeque<int> Times10Plus(this IDeque<int> d, int j) => d.Op(i => i * 10 + j);
static IEnumerable<T> EnumerateFromLeft(this IDeque<T> d)
{
  var c = d;
  while (!c.IsEmpty) 
  { 
    yield return c.PeekLeft(); 
    c = c.DequeueLeft(); 
  }
}