2017-08-13 48 views
2

我试图用流利的接口来构建一个集合,类似这样(简体)例如:如何使用流畅的界面构建序列?

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

我能想出add添加(最好的解决方案)为:

IEnumerable<T> Add<T>(this IEnumerable<T> coll, T item) 
    { 
    foreach(var t in coll) yield return t; 
    yield return item; 
    } 

这似乎增加了每次调用中要重复的大量开销。

有没有更好的方法?

更新: 在我的冲刺中,我过分简化了这个例子,并且忽略了一个重要的要求。现有coll中的最后一项影响下一个项目。所以,一个稍微不那么简单的例子:

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; 
} 
+0

有趣的问题。但是,我不确定是否有更好的方法可以流利地做到这一点。你可以使'Add'方法带一个'params'数组并且一次添加它们,但是这并不能真正回答这个问题。 – DavidG

+0

我不认为有。除非你保留一些内部缓冲,例如使用'ImmutableList '或类似的东西。 –

回答

1

你可以做到以下几点:

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

请注意,我使用IReadOnlyList使其能够索引到列表中的一个性能方法,并如果需要可以获得最后一个元素。如果你需要一个懒惰枚举,那么我认为你坚持你最初的想法。

果然,如下:

var a = 1.Starts().Times10Plus(2).Times10Plus(3).Times10Plus(4).ToArray(); 

产生预期的结果({1, 12, 123, 1234})有,我认为是合理的表现。

+0

现在所有这些方法调用都只是将同一个引用返回给单个可变(和变异)的数据结构。所以如果你写'var start = Starts(1); var nextValue = start.Times10Plus(2); Console.WriteLine(start.Count);'它会打印2,而不是1,这是错误的。 – Servy

+0

@servy是的,我知道,但从问题来看,这是不是很不清楚。无论如何,这是很容易解决的,只是使用不可变队列作为内部类型,就是这样。如果枚举不是关键性能的,你甚至可以使用一个简单的直接不可变的堆栈,它按照相反的顺序枚举。 – InBetween

+0

换句话说,你现在回到使用OP的解决方案。 – Servy