@@ -346,25 +346,92 @@ public bool Remove(TKey key)
346346 }
347347
348348 /// <inheritdoc cref="IEnumerable{T}.GetEnumerator"/>
349- public IEnumerator < KeyValuePair < TKey , TValue > > GetEnumerator ( )
349+ [ Pure ]
350+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
351+ public Enumerator GetEnumerator ( ) => new ( this ) ;
352+
353+ /// <summary>
354+ /// A custom enumerator that traverses items in a <see cref="ConditionalWeakTable{TKey, TValue}"/> instance.
355+ /// </summary>
356+ public ref struct Enumerator
350357 {
351- for ( LinkedListNode < WeakReference < TKey > > ? node = this . keys . First ; ! ( node is null ) ; )
358+ /// <summary>
359+ /// The owner <see cref="ConditionalWeakTable{TKey, TValue}"/> instance for the enumerator.
360+ /// </summary>
361+ private readonly ConditionalWeakTable < TKey , TValue > owner ;
362+
363+ /// <summary>
364+ /// The current <see cref="LinkedListNode{T}"/>, if any.
365+ /// </summary>
366+ private LinkedListNode < WeakReference < TKey > > ? node ;
367+
368+ /// <summary>
369+ /// The current <see cref="KeyValuePair{TKey, TValue}"/> to return.
370+ /// </summary>
371+ private KeyValuePair < TKey , TValue > current ;
372+
373+ /// <summary>
374+ /// Indicates whether or not <see cref="MoveNext"/> has been called at least once.
375+ /// </summary>
376+ private bool isFirstMoveNextPending ;
377+
378+ /// <summary>
379+ /// Initializes a new instance of the <see cref="Enumerator"/> struct.
380+ /// </summary>
381+ /// <param name="owner">The owner <see cref="ConditionalWeakTable{TKey, TValue}"/> instance for the enumerator.</param>
382+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
383+ public Enumerator ( ConditionalWeakTable < TKey , TValue > owner )
352384 {
353- LinkedListNode < WeakReference < TKey > > ? next = node . Next ;
385+ this . owner = owner ;
386+ this . node = null ;
387+ this . current = default ;
388+ this . isFirstMoveNextPending = true ;
389+ }
390+
391+ /// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
392+ public bool MoveNext ( )
393+ {
394+ LinkedListNode < WeakReference < TKey > > ? node ;
354395
355- // Get the key and value for the current node
356- if ( node . Value . TryGetTarget ( out TKey ? target ) &&
357- this . table . TryGetValue ( target ! , out TValue value ) )
396+ if ( ! isFirstMoveNextPending )
358397 {
359- yield return new ( target , value ) ;
398+ node = this . node ! . Next ;
360399 }
361400 else
362401 {
363- // If the current key has been collected, trim the list
364- this . keys . Remove ( node ) ;
402+ node = this . owner . keys . First ;
403+
404+ this . isFirstMoveNextPending = false ;
365405 }
366406
367- node = next ;
407+ while ( node is not null )
408+ {
409+ // Get the key and value for the current node
410+ if ( node . Value . TryGetTarget ( out TKey ? target ) &&
411+ this . owner . table . TryGetValue ( target ! , out TValue value ) )
412+ {
413+ this . node = node ;
414+ this . current = new KeyValuePair < TKey , TValue > ( target , value ) ;
415+
416+ return true ;
417+ }
418+ else
419+ {
420+ // If the current key has been collected, trim the list
421+ this . owner . keys . Remove ( node ) ;
422+ }
423+
424+ node = node . Next ;
425+ }
426+
427+ return false ;
428+ }
429+
430+ /// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
431+ public readonly KeyValuePair < TKey , TValue > Current
432+ {
433+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
434+ get => this . current ;
368435 }
369436 }
370437 }
0 commit comments