1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Diagnostics; 7 using System.Collections; 8 using System.Collections.Generic; 9 10 namespace System.DirectoryServices.AccountManagement 11 { 12 internal class TrackedCollectionEnumerator<T> : IEnumerator, IEnumerator<T> 13 { 14 // 15 // Public properties 16 // 17 18 public T Current 19 { 20 get 21 { 22 CheckDisposed(); 23 24 // Since MoveNext() saved off the current value for us, this is largely trivial. 25 26 if (_endReached == true || _enumerator == null) 27 { 28 // Either we're at the end or before the beginning 29 GlobalDebug.WriteLineIf(GlobalDebug.Warn, "TrackedCollectionEnumerator", "Current: bad position, endReached={0}", _endReached); 30 throw new InvalidOperationException(SR.TrackedCollectionEnumInvalidPos); 31 } 32 33 return _current; 34 } 35 } 36 37 object IEnumerator.Current 38 { 39 get 40 { 41 return Current; 42 } 43 } 44 45 // 46 // Public methods 47 // 48 MoveNext()49 public bool MoveNext() 50 { 51 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "Entering MoveNext"); 52 53 CheckDisposed(); 54 CheckChanged(); 55 56 if (_endReached) 57 { 58 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: endReached"); 59 return false; 60 } 61 62 if (_enumerator == null) 63 { 64 // Must be at the very beginning 65 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: at beginning"); 66 67 _enumerator = ((IEnumerable)_combinedValues).GetEnumerator(); 68 Debug.Assert(_enumerator != null); 69 } 70 71 bool gotNextValue = _enumerator.MoveNext(); 72 73 // If we got the next value, 74 // save it off so that Current can later return it. 75 if (gotNextValue) 76 { 77 // Have to handle differently, since inserted values are just a T, while 78 // original value are a Pair<T,T>, where Pair.Right is the current value 79 TrackedCollection<T>.ValueEl el = (TrackedCollection<T>.ValueEl)_enumerator.Current; 80 81 if (el.isInserted) 82 { 83 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: current ({0}) is inserted", _current); 84 _current = el.insertedValue; 85 } 86 else 87 { 88 _current = el.originalValue.Right; 89 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: current ({0}) is original", _current); 90 } 91 } 92 else 93 { 94 // Nothing more to enumerate 95 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "MoveNext: nothing more to enumerate"); 96 97 _endReached = true; 98 } 99 100 return gotNextValue; 101 } 102 IEnumerator.MoveNext()103 bool IEnumerator.MoveNext() 104 { 105 return MoveNext(); 106 } 107 Reset()108 public void Reset() 109 { 110 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "Reset"); 111 112 CheckDisposed(); 113 CheckChanged(); 114 115 _endReached = false; 116 _enumerator = null; 117 } 118 IEnumerator.Reset()119 void IEnumerator.Reset() 120 { 121 Reset(); 122 } 123 Dispose()124 public void Dispose() 125 { 126 _disposed = true; 127 } 128 129 // 130 // Internal constructors 131 // TrackedCollectionEnumerator(string outerClassName, TrackedCollection<T> trackedCollection, List<TrackedCollection<T>.ValueEl> combinedValues)132 internal TrackedCollectionEnumerator(string outerClassName, TrackedCollection<T> trackedCollection, List<TrackedCollection<T>.ValueEl> combinedValues) 133 { 134 GlobalDebug.WriteLineIf(GlobalDebug.Info, "TrackedCollectionEnumerator", "Ctor"); 135 136 _outerClassName = outerClassName; 137 _trackedCollection = trackedCollection; 138 _combinedValues = combinedValues; 139 } 140 141 // 142 // Private implementation 143 // 144 145 // Have we been disposed? 146 private bool _disposed = false; 147 148 // The name of our outer class. Used when throwing an ObjectDisposedException. 149 private string _outerClassName; 150 151 private List<TrackedCollection<T>.ValueEl> _combinedValues = null; 152 153 // The value we're currently positioned at 154 private T _current; 155 156 // The enumerator for our inner list, combinedValues. 157 private IEnumerator _enumerator = null; 158 159 // True when we reach the end of combinedValues (no more values to enumerate in the TrackedCollection) 160 private bool _endReached = false; 161 162 // When this enumerator was constructed, to detect changes made to the TrackedCollection after it was constructed 163 private DateTime _creationTime = DateTime.UtcNow; 164 private TrackedCollection<T> _trackedCollection = null; 165 CheckDisposed()166 private void CheckDisposed() 167 { 168 if (_disposed) 169 { 170 GlobalDebug.WriteLineIf(GlobalDebug.Warn, "TrackedCollectionEnumerator", "CheckDisposed: accessing disposed object"); 171 throw new ObjectDisposedException(_outerClassName); 172 } 173 } 174 CheckChanged()175 private void CheckChanged() 176 { 177 // Make sure the app hasn't changed our underlying list 178 if (_trackedCollection.LastChange > _creationTime) 179 { 180 GlobalDebug.WriteLineIf( 181 GlobalDebug.Warn, 182 "TrackedCollectionEnumerator", 183 "CheckChanged: has changed (last change={0}, creation={1})", 184 _trackedCollection.LastChange, 185 _creationTime); 186 187 throw new InvalidOperationException(SR.TrackedCollectionEnumHasChanged); 188 } 189 } 190 } 191 } 192 193