2005-04-19 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Web / System.Web.Caching / ExpiresBuckets.cs
1 // \r
2 // System.Web.Caching\r
3 //\r
4 // Author:\r
5 //   Patrik Torstensson (Patrik.Torstensson@labs2.com)\r
6 //   Daniel Cazzulino [DHC] (dcazzulino@users.sf.net)\r
7 //\r
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 \r
30 using System;\r
31 using System.Collections;\r
32 using System.Threading;\r
33 \r
34 namespace System.Web.Caching {\r
35         /// <summary>\r
36         /// Responsible for holding a cache entry in the linked list bucket.\r
37         /// </summary>\r
38         internal struct ExpiresEntry {\r
39                 internal CacheEntry Entry;\r
40                 internal long TicksExpires;\r
41                 internal int _intNext;\r
42         }\r
43 \r
44         /// <summary>\r
45         /// Holds cache entries that has a expiration in a bucket list.\r
46         /// </summary>\r
47         internal class ExpiresBucket {\r
48                 private static int MIN_ENTRIES = 4;\r
49                 \r
50                 private byte _byteID;\r
51                 private int     _intSize;\r
52                 private int     _intCount;\r
53                 private int     _intNext;\r
54 \r
55                 private Cache _objManager;\r
56 \r
57                 private ExpiresEntry [] _arrEntries;\r
58 \r
59                 private System.Threading.ReaderWriterLock _lock = new System.Threading.ReaderWriterLock();\r
60 \r
61                 /// <summary>\r
62                 /// Keeps a list of indexes in the list which are available to place new items. [DHC]\r
63                 /// </summary>\r
64                 private Int32Collection _freeidx = new Int32Collection();\r
65 \r
66                 /// <summary>\r
67                 /// Constructs a new bucket.\r
68                 /// </summary>\r
69                 /// <param name="bucket">Current bucket ID.</param>\r
70                 /// <param name="objManager">Cache manager reponsible for the item(s) in the expires bucket.</param>\r
71                 internal ExpiresBucket (byte bucket, Cache objManager) {\r
72                         _objManager = objManager;\r
73                         Initialize(bucket);\r
74                 }\r
75 \r
76                 /// <summary>\r
77                 /// Initializes the expires bucket, creates a linked list of MIN_ENTRIES.\r
78                 /// </summary>\r
79                 /// <param name="bucket">Bucket ID.</param>\r
80                 private void Initialize (byte bucket) {\r
81                         _byteID = bucket;\r
82                         _intNext = 0;\r
83                         _intCount = 0;\r
84 \r
85                         _arrEntries = new ExpiresEntry [MIN_ENTRIES];\r
86                         _intSize = MIN_ENTRIES;\r
87 \r
88                         int intPos = 0;\r
89                         do {\r
90                                 _arrEntries[intPos]._intNext = intPos + 1;\r
91                                 _arrEntries[intPos].TicksExpires = Cache.NoAbsoluteExpiration.Ticks;\r
92                                 \r
93                                 intPos++;\r
94                         } while (intPos < _intSize);\r
95 \r
96                         _arrEntries[_intSize - 1]._intNext = -1;\r
97                 }\r
98 \r
99                 /// <summary>\r
100                 /// Expands the bucket linked array list.\r
101                 /// </summary>\r
102                 private void Expand () {\r
103                         _lock.AcquireWriterLock(-1);\r
104                         try {\r
105                                 int oldsize = _intSize;\r
106                                 _intSize *= 2;\r
107 \r
108                                 // Copy items to the new list.\r
109                                 ExpiresEntry[] newlist = new ExpiresEntry[_intSize];\r
110                                 _arrEntries.CopyTo(newlist, 0);\r
111 \r
112                                 // Set last element to point to the next new empty element\r
113                                 _intNext = oldsize;\r
114                                 newlist[oldsize - 1]._intNext = oldsize;\r
115 \r
116                                 // Initialize positions for the rest of new elements.\r
117                                 for (int i = oldsize; i < _intSize; i++) {\r
118                                         newlist[i]._intNext = i + 1;\r
119                                         newlist[i].TicksExpires = Cache.NoAbsoluteExpiration.Ticks;\r
120                                 }\r
121 \r
122                                 // Last item signals the expansion of the list.\r
123                                 newlist[_intSize - 1]._intNext = -1;\r
124 \r
125                                 // Replace the existing list.\r
126                                 _arrEntries = newlist;\r
127                         }\r
128                         finally {\r
129                                 _lock.ReleaseWriterLock();\r
130                         }\r
131 \r
132                 }\r
133 \r
134                 /// <summary>\r
135                 /// Adds a cache entry into the expires bucket.\r
136                 /// </summary>\r
137                 /// <param name="objEntry">Cache Entry object to be added.</param>\r
138                 internal void Add (CacheEntry objEntry) {\r
139                         _lock.AcquireWriterLock(-1);\r
140                         try {\r
141                                 if (_intNext == -1) {\r
142                                         if (_freeidx.Count == 0)\r
143                                                 Expand();\r
144                                         else {\r
145                                                 _intNext = _freeidx[0];\r
146                                                 _freeidx.Remove(_intNext);\r
147                                         }\r
148                                 }\r
149                         \r
150                                 _arrEntries[_intNext].TicksExpires = objEntry.Expires;\r
151                                 _arrEntries[_intNext].Entry = objEntry;\r
152 \r
153                                 objEntry.ExpiresBucket = _byteID;\r
154                                 objEntry.ExpiresIndex = _intNext;\r
155 \r
156                                 // If there are free indexes in the list, reuse them for the _next value.\r
157                                 if (_freeidx.Count != 0) {\r
158                                         _intNext = _freeidx [0];\r
159                                         _freeidx.Remove(_intNext);\r
160                                 } else\r
161                                         _intNext = _arrEntries[_intNext]._intNext;\r
162 \r
163                                 _intCount++;\r
164                         }\r
165                         finally {\r
166                                 _lock.ReleaseWriterLock();\r
167                         }\r
168                 }\r
169 \r
170                 /// <summary>\r
171                 /// Removes a cache entry from the expires bucket.\r
172                 /// </summary>\r
173                 /// <param name="objEntry">Cache entry to be removed.</param>\r
174                 internal void Remove(CacheEntry objEntry) {\r
175                         // Check if this is our bucket\r
176                         if (objEntry.ExpiresBucket != _byteID) return;\r
177                         if (objEntry.ExpiresIndex == CacheEntry.NoIndexInBucket) return;\r
178 \r
179                         _lock.AcquireWriterLock(-1);\r
180                         try {\r
181                                 if (_arrEntries.Length < objEntry.ExpiresIndex) return;\r
182                                 _intCount--;\r
183 \r
184                                 // Push the index as a free one.\r
185                                 _freeidx.Add(objEntry.ExpiresIndex);\r
186 \r
187                                 _arrEntries[objEntry.ExpiresIndex].Entry = null;\r
188                                 // Clear bucket-related values from the item.\r
189                                 objEntry.ExpiresBucket = CacheEntry.NoBucketHash;\r
190                                 objEntry.ExpiresIndex = CacheEntry.NoIndexInBucket;\r
191                         }\r
192                         finally {\r
193                                 //Releases both reader & writer locks\r
194                                 _lock.ReleaseWriterLock();\r
195                         }\r
196                 }\r
197 \r
198                 /// <summary>\r
199                 /// Updates a cache entry in the expires bucket, this is called during a hit of an item if the \r
200                 /// cache item has a sliding expiration. The function is responsible for updating the cache\r
201                 /// entry.\r
202                 /// </summary>\r
203                 /// <param name="objEntry">Cache entry to update.</param>\r
204                 /// <param name="ticksExpires">New expiration value for the cache entry.</param>\r
205                 internal void Update(CacheEntry objEntry, long ticksExpires) {\r
206                         // Check if this is our bucket\r
207                         if (objEntry.ExpiresBucket != _byteID) return;\r
208                         if (objEntry.ExpiresIndex == CacheEntry.NoIndexInBucket) return;\r
209 \r
210                         _lock.AcquireWriterLock(-1);\r
211                         try {\r
212                                 if (_arrEntries.Length < objEntry.ExpiresIndex) return;\r
213 \r
214                                 // Proceed to update.\r
215                                 _arrEntries[objEntry.ExpiresIndex].TicksExpires = ticksExpires;\r
216                                 _arrEntries[objEntry.ExpiresIndex].Entry.Expires = ticksExpires;\r
217                         }\r
218                         finally {\r
219                                 //Releases both read & write locks\r
220                                 _lock.ReleaseWriterLock();\r
221                         }\r
222                 }\r
223 \r
224                 /// <summary>\r
225                 /// Flushes all cache entries that has expired and removes them from the cache manager.\r
226                 /// </summary>\r
227                 internal void FlushExpiredItems() {\r
228                         ExpiresEntry objEntry;\r
229                         ArrayList removeList = null;\r
230                         ArrayList flushList = null;\r
231                         int     intPos;\r
232                         long ticksNow;\r
233         \r
234                         ticksNow = DateTime.UtcNow.Ticks;\r
235 \r
236                         intPos = 0;\r
237                         // Lookup all items that needs to be removed, this is done in a two part\r
238                         // operation to minimize the locking time.\r
239                         _lock.AcquireReaderLock (-1);\r
240                         try {\r
241                                 do {\r
242                                         objEntry = _arrEntries [intPos];\r
243                                         if (null != objEntry.Entry && \r
244                                                 ((objEntry.TicksExpires < ticksNow) || objEntry.Entry.ExpiresBucket != _byteID))\r
245                                         {\r
246                                                 if (null == removeList)\r
247                                                         removeList = new ArrayList ();\r
248                                                 \r
249                                                 removeList.Add (objEntry);\r
250                                         }\r
251                                                 \r
252                                         intPos++;\r
253                                 } while (intPos < _intSize);\r
254                         }\r
255                         finally {\r
256                                 _lock.ReleaseReaderLock ();\r
257                         }                       \r
258 \r
259                         if (null != removeList) {\r
260                                 flushList = new ArrayList (removeList.Count);\r
261 \r
262                                 _lock.AcquireWriterLock (-1);   \r
263                                 try {\r
264                                         foreach (ExpiresEntry entry in removeList) { \r
265                                                 ExpiresEntry e = entry;\r
266                                                 int id = entry.Entry.ExpiresIndex;\r
267 \r
268                                                 //push the index for reuse\r
269                                                 _freeidx.Add (id);\r
270 \r
271                                                 if (entry.Entry.ExpiresBucket == _byteID) {\r
272                                                         // add to our flush list\r
273                                                         flushList.Add (e.Entry);\r
274 \r
275                                                         // Remove from bucket\r
276                                                         e.Entry.ExpiresBucket = CacheEntry.NoBucketHash;\r
277                                                         e.Entry.ExpiresIndex = CacheEntry.NoIndexInBucket;\r
278                                                 } \r
279                                                 \r
280                                                 e.Entry = null;\r
281                                                 \r
282                                                 // Entries is structs, put it back\r
283                                                 _arrEntries [id] = e;\r
284                                         }\r
285                                 }\r
286                                 finally {\r
287                                         _lock.ReleaseWriterLock ();\r
288                                 }\r
289 \r
290                                 // We can call this without locks, it can takes time due to callbacks to user code\r
291                                 foreach (CacheEntry entry in flushList)\r
292                                         _objManager.Remove (entry.Key, CacheItemRemovedReason.Expired);\r
293 \r
294                                 flushList = null;\r
295                                 removeList = null;\r
296                         }\r
297                 }\r
298 \r
299                 /// <summary>\r
300                 /// Returns the current size of the expires bucket.\r
301                 /// </summary>\r
302                 internal int Size {\r
303                         get { \r
304                                 return _intSize;\r
305                         }\r
306                 }\r
307 \r
308                 /// <summary>\r
309                 /// Returns number of items in the bucket.\r
310                 /// </summary>\r
311                 internal int Count {\r
312                         get {\r
313                                 return  _intCount;\r
314                         }\r
315                 }\r
316 \r
317                 #region Private Int32Collection\r
318                 /* This file has been automatically generated by TextBox -- DO NOT EDIT! */\r
319                 /*\r
320                 Int32Collection\r
321                 Int32Collection.Enumerator\r
322 \r
323                 These C# classes implement a strongly-typed collection of \r
324                 Int32 objects.\r
325 \r
326                 The internal representation is an array of Int32, so the performance \r
327                 characteristics will be more like a vector than a list, to use STL terminology.\r
328 \r
329                 The implementation is optimized for value-types, as it goes to great length to \r
330                 avoid the overhead of boxing and unboxing.  But it should also work well for \r
331                 reference types.\r
332 \r
333                 Mad props to Nick Wienholt <sheyenne@bigpond.com> for assisting me in \r
334                 this research, and the testing, the benchmarking, and of course, the \r
335                 implementation!\r
336 \r
337                 Last but not least, a quick shout out to Kit George, for his generous \r
338                 contribution to the dotnet mailing list -- a code-generator for \r
339                 CollectionBase-derived classes:\r
340                         http://discuss.develop.com/archives/wa.exe?A2=ind0107C&L=DOTNET&P=R35911\r
341                 This was the original inspiration for the fine code you are now enjoying.\r
342 \r
343                 - Shawn Van Ness\r
344 \r
345                 Other folks who've contributed:\r
346                         Ethan Smith <ethan.smith@pobox.com> (minor perf. improvements)\r
347                         Joel Mueller <jmueller@swiftk.com> (major perf. improvements)\r
348                         Chris Sells <csells@sellsbrothers.com> (generative programming guru)\r
349                         Patrice Lafond <plafond@hemisphere.bm> (a bug fix -- yikes!)\r
350                 */\r
351 \r
352                 /// <summary>\r
353                 /// An optimized collection for holding <see cref="Int32"/> values.\r
354                 /// </summary>\r
355                 [System.Serializable]\r
356                         private class Int32Collection : System.Collections.ICollection, System.Collections.IList, System.Collections.IEnumerable {\r
357                         #region Private vars & ctors\r
358                         private const int DefaultMinimumCapacity = 16;\r
359 \r
360                         private System.Int32[] m_array = new System.Int32[DefaultMinimumCapacity];\r
361                         private int m_count = 0;\r
362                         private int m_version = 0;\r
363 \r
364                         /// <summary />\r
365                         public Int32Collection() {\r
366                         }\r
367 \r
368                         /// <summary />\r
369                         public Int32Collection(Int32Collection collection) {\r
370                                 AddRange(collection); }\r
371 \r
372                         /// <summary />\r
373                         public Int32Collection(System.Int32[] array) {\r
374                                 AddRange(array); }\r
375                         #endregion\r
376 \r
377                         #region Public members\r
378 \r
379                         /// <summary />\r
380                         public int Count {\r
381                                 get {\r
382                                         return m_count; }\r
383                         }\r
384 \r
385                         /// <summary />\r
386                         public void CopyTo(System.Int32[] array) {\r
387                                 this.CopyTo(array, 0);\r
388                         }\r
389 \r
390                         /// <summary />\r
391                         public void CopyTo(System.Int32[] array, int start) {\r
392                                 if (m_count > array.GetUpperBound(0)+1-start)\r
393                                         throw new System.ArgumentException("Destination array was not long enough.");\r
394 \r
395                                 // for (int i=0; i < m_count; ++i) array[start+i] = m_array[i];\r
396                                 System.Array.Copy(m_array, 0, array, start, m_count); \r
397                         }\r
398 \r
399                         /// <summary />\r
400                         public System.Int32 this[int index] {\r
401                                 get {\r
402                                         ValidateIndex(index); // throws\r
403                                         return m_array[index]; \r
404                                 }\r
405                                 set {\r
406                                         ValidateIndex(index); // throws\r
407                                 \r
408                                         ++m_version; \r
409                                         m_array[index] = value; \r
410                                 }\r
411                         }\r
412 \r
413                         /// <summary />\r
414                         public int Add(System.Int32 item) {                     \r
415                                 if (NeedsGrowth())\r
416                                         Grow();\r
417 \r
418                                 ++m_version;\r
419                                 m_array[m_count] = item;\r
420                         \r
421                                 return m_count++;\r
422                         }\r
423                 \r
424                         /// <summary />\r
425                         public void Clear() {\r
426                                 ++m_version;\r
427                                 m_array = new System.Int32[DefaultMinimumCapacity];\r
428                                 m_count = 0;\r
429                         }\r
430 \r
431                         /// <summary />\r
432                         public bool Contains(System.Int32 item) {\r
433                                 return ((IndexOf(item) == -1)?false:true);\r
434                         }\r
435 \r
436                         /// <summary />\r
437                         public int IndexOf(System.Int32 item) {\r
438                                 for (int i=0; i < m_count; ++i)\r
439                                         if (m_array[i].Equals(item))\r
440                                                 return i;\r
441                                 return -1;\r
442                         }\r
443 \r
444                         /// <summary />\r
445                         public void Insert(int position, System.Int32 item) {\r
446                                 ValidateIndex(position,true); // throws\r
447                         \r
448                                 if (NeedsGrowth())\r
449                                         Grow();\r
450 \r
451                                 ++m_version;\r
452                                 System.Array.Copy(m_array, position, m_array, position+1, m_count-position);\r
453 \r
454                                 m_array[position] = item;\r
455                                 m_count++;\r
456                         }\r
457 \r
458                         /// <summary />\r
459                         public void Remove(System.Int32 item) {                 \r
460                                 int index = IndexOf(item);\r
461                                 if (index < 0)\r
462                                         throw new System.ArgumentException("Cannot remove the specified item because it was not found in the specified Collection.");\r
463                         \r
464                                 RemoveAt(index);\r
465                         }\r
466 \r
467                         /// <summary />\r
468                         public void RemoveAt(int index) {\r
469                                 ValidateIndex(index); // throws\r
470                         \r
471                                 ++m_version;\r
472                                 m_count--;\r
473                                 System.Array.Copy(m_array, index+1, m_array, index, m_count-index);\r
474 \r
475                                 if (NeedsTrimming())\r
476                                         Trim();\r
477                         }\r
478 \r
479                         // Public helpers (just to mimic some nice features of ArrayList)\r
480 \r
481                         /// <summary />\r
482                         public int Capacity {\r
483                                 get {\r
484                                         return m_array.Length; }\r
485                                 set {\r
486                                         if (value < m_count) value = m_count;\r
487                                         if (value < DefaultMinimumCapacity) value = DefaultMinimumCapacity;\r
488 \r
489                                         if (m_array.Length == value) return;\r
490 \r
491                                         ++m_version;\r
492 \r
493                                         System.Int32[] temp = new System.Int32[value];\r
494                                         System.Array.Copy(m_array, 0, temp, 0, m_count); \r
495                                         m_array = temp;\r
496                                 }\r
497                         }\r
498 \r
499                         /// <summary />\r
500                         public void AddRange(Int32Collection collection) {\r
501                                 ++m_version;\r
502 \r
503                                 Capacity += collection.Count;\r
504                                 System.Array.Copy(collection.m_array, 0, this.m_array, m_count, collection.m_count);\r
505                                 m_count += collection.Count;\r
506                         }\r
507 \r
508                         /// <summary />\r
509                         public void AddRange(System.Int32[] array) {\r
510                                 ++m_version;\r
511 \r
512                                 Capacity += array.Length;\r
513                                 System.Array.Copy(array, 0, this.m_array, m_count, array.Length);\r
514                                 m_count += array.Length;\r
515                         }\r
516                         #endregion\r
517 \r
518                         #region Private helper methods\r
519                         private void ValidateIndex(int index) {\r
520                                 ValidateIndex(index,false);\r
521                         }\r
522 \r
523                         private void ValidateIndex(int index, bool allowEqualEnd) {\r
524                                 int max = (allowEqualEnd)?(m_count):(m_count-1);\r
525                                 if (index < 0 || index > max)\r
526                                         throw new System.ArgumentOutOfRangeException("Index was out of range.  Must be non-negative and less than the size of the collection.", (object)index, "Specified argument was out of the range of valid values.");\r
527                         }\r
528 \r
529                         private bool NeedsGrowth() {\r
530                                 return (m_count >= Capacity);\r
531                         }\r
532 \r
533                         private void Grow() {\r
534                                 if (NeedsGrowth())\r
535                                         Capacity = m_count*2;\r
536                         }\r
537 \r
538                         private bool NeedsTrimming() {\r
539                                 return (m_count <= Capacity/2);\r
540                         }\r
541 \r
542                         private void Trim() {\r
543                                 if (NeedsTrimming())\r
544                                         Capacity = m_count;\r
545                         }\r
546                         #endregion\r
547 \r
548                         #region System.Collections.ICollection implementation\r
549                         bool System.Collections.ICollection.IsSynchronized {\r
550                                 get {\r
551                                         return m_array.IsSynchronized; }\r
552                         }\r
553 \r
554                         object System.Collections.ICollection.SyncRoot {\r
555                                 get {\r
556                                         return m_array.SyncRoot; }\r
557                         }\r
558 \r
559                         void System.Collections.ICollection.CopyTo(System.Array array, int start) {\r
560                                 this.CopyTo((System.Int32[])array, start);\r
561                         }\r
562                         #endregion\r
563 \r
564                         #region System.Collections.IList implementation\r
565                         bool System.Collections.IList.IsFixedSize {\r
566                                 get {\r
567                                         return false; }\r
568                         }\r
569 \r
570                         bool System.Collections.IList.IsReadOnly {\r
571                                 get {\r
572                                         return false; }\r
573                         }\r
574 \r
575                         object System.Collections.IList.this[int index] {\r
576                                 get { return (object)this[index]; }\r
577                                 set { this[index] = (System.Int32)value; }\r
578                         }\r
579 \r
580                         int System.Collections.IList.Add(object item) {\r
581                                 return this.Add((System.Int32)item);\r
582                         }\r
583 \r
584                         bool System.Collections.IList.Contains(object item) {\r
585                                 return this.Contains((System.Int32)item);\r
586                         }\r
587 \r
588                         int System.Collections.IList.IndexOf(object item) {\r
589                                 return this.IndexOf((System.Int32)item);\r
590                         }\r
591 \r
592                         void System.Collections.IList.Insert(int position, object item) {\r
593                                 this.Insert(position, (System.Int32)item);\r
594                         }\r
595 \r
596                         void System.Collections.IList.Remove(object item) {\r
597                                 this.Remove((System.Int32)item);\r
598                         }\r
599                         #endregion\r
600 \r
601                         #region System.Collections.IEnumerable and enumerator implementation\r
602                         /// <summary />\r
603                         public Enumerator GetEnumerator() {\r
604                                 return new Enumerator(this);\r
605                         }\r
606 \r
607                         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {\r
608                                 return GetEnumerator();\r
609                         }\r
610 \r
611                         // Nested enumerator class\r
612                         /// <summary />\r
613                         public class Enumerator : System.Collections.IEnumerator {\r
614                                 private Int32Collection m_collection;\r
615                                 private int m_index;\r
616                                 private int m_version;\r
617 \r
618                                 // Construction\r
619         \r
620                                 /// <summary />\r
621                                 public Enumerator(Int32Collection tc) {\r
622                                         m_collection = tc;\r
623                                         m_index = -1;\r
624                                         m_version = tc.m_version;\r
625                                 }\r
626         \r
627                                 /// <summary />\r
628                                 public System.Int32 Current {\r
629                                         get {\r
630                                                 return m_collection[m_index]; }\r
631                                 }\r
632         \r
633                                 /// <summary />\r
634                                 public bool MoveNext() {\r
635                                         if (m_version != m_collection.m_version)\r
636                                                 throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute.");\r
637 \r
638                                         ++m_index;\r
639                                         return (m_index < m_collection.Count)?true:false;\r
640                                 }\r
641         \r
642                                 /// <summary />\r
643                                 public void Reset() {\r
644                                         if (m_version != m_collection.m_version)\r
645                                                 throw new System.InvalidOperationException("Collection was modified; enumeration operation may not execute.");\r
646 \r
647                                         m_index = -1;\r
648                                 }\r
649 \r
650                                 object System.Collections.IEnumerator.Current {\r
651                                         get {\r
652                                                 return (object)(this.Current); }\r
653                                 }\r
654                         }\r
655                         #endregion\r
656                 }\r
657                 #endregion\r
658         }\r
659 }\r
660