2017-08-13 38 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