Lean  $LEAN_TAG$
MemoizingEnumerable.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15 */
16 
17 using System.Collections;
18 using System.Collections.Generic;
19 
20 namespace QuantConnect.Util
21 {
22  /// <summary>
23  /// Defines an enumerable that can be enumerated many times while
24  /// only performing a single enumeration of the root enumerable
25  /// </summary>
26  /// <typeparam name="T"></typeparam>
27  public class MemoizingEnumerable<T> : IEnumerable<T>
28  {
29  private List<T> _buffer;
30  private IEnumerator<T> _enumerator;
31 
32  /// <summary>
33  /// Allow disableing the buffering
34  /// </summary>
35  /// <remarks>Should be called before the enumeration starts</remarks>
36  public bool Enabled { get; set; }
37 
38  /// <summary>
39  /// Initializes a new instance of the <see cref="MemoizingEnumerable{T}"/> class
40  /// </summary>
41  /// <param name="enumerable">The source enumerable to be memoized</param>
42  public MemoizingEnumerable(IEnumerable<T> enumerable)
43  {
44  Enabled = true;
45  _enumerator = enumerable.GetEnumerator();
46  }
47 
48  /// <summary>
49  /// Returns an enumerator that iterates through the collection.
50  /// </summary>
51  /// <returns>
52  /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
53  /// </returns>
54  /// <filterpriority>1</filterpriority>
55  public IEnumerator<T> GetEnumerator()
56  {
57  if (!Enabled)
58  {
59  if (_enumerator != null)
60  {
61  while (_enumerator.MoveNext())
62  {
63  yield return _enumerator.Current;
64  }
65 
66  // important to avoid leak!
67  _enumerator.Dispose();
68  _enumerator = null;
69  }
70  }
71  else
72  {
73  if (_buffer == null)
74  {
75  // lazy create our buffer
76  _buffer = new List<T>();
77  }
78 
79  int i = 0;
80  while (i <= _buffer.Count)
81  {
82  // sync for multiple threads access to _enumerator and _buffer
83  lock (_buffer)
84  {
85  // check to see if we need to move next
86  if (_enumerator != null && i >= _buffer.Count)
87  {
88  if (_enumerator.MoveNext())
89  {
90  var value = _enumerator.Current;
91  _buffer.Add(value);
92  yield return value;
93  }
94  else
95  {
96  // important to avoid leak!
97  _enumerator.Dispose();
98  _enumerator = null;
99  }
100  }
101  else
102  {
103  // we have a value if it's in the buffer
104  if (_buffer.Count > i)
105  {
106  yield return _buffer[i];
107  }
108  }
109  }
110 
111  // increment for next time
112  i++;
113  }
114  }
115  }
116 
117  /// <summary>
118  /// Returns an enumerator that iterates through a collection.
119  /// </summary>
120  /// <returns>
121  /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
122  /// </returns>
123  /// <filterpriority>2</filterpriority>
124  IEnumerator IEnumerable.GetEnumerator()
125  {
126  return GetEnumerator();
127  }
128  }
129 }