File: System\Windows\Media\Animation\WeakRefEnumerator.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
 
namespace System.Windows.Media.Animation
{
    /// <summary>
    /// An enumerator for collections of weak references.
    /// </summary>
    /// <remarks>
    /// The enumerator returns only live objects and removes dead
    /// references from the list as it walks.
    /// </remarks>
    internal struct WeakRefEnumerator<T>
    {
        #region Internal implementation
 
        #region Construction
 
        /// <summary>
        /// Creates an enumerator for the given list.
        /// </summary>
        /// <param name="list">
        /// The list to enumerate.
        /// </param>
        internal WeakRefEnumerator(List<WeakReference> list)
        {
            _list = list;
            _readIndex = 0;
            _writeIndex = 0;
            _current = default(T);
 
#if DEBUG
            _valid = false;
#endif // DEBUG
        }
 
        #endregion // Construction
 
        #region Properties
 
        /// <summary>
        /// Gets the current element in the collection.
        /// </summary>
        /// <value>
        /// The current element in the collection.
        /// </value>
        internal T Current
        {
            get
            {
#if DEBUG
                Debug.Assert(_valid);
#endif // DEBUG
 
                return _current;
            }
        }
 
        /// <summary>
        /// Gets the index of the current element in the collection.
        /// </summary>
        /// <value>
        /// The index of the current element in the collection.
        /// </value>
        internal int CurrentIndex
        {
            get
            {
#if DEBUG
                Debug.Assert(_valid);
#endif // DEBUG
 
                return _writeIndex - 1;
            }
        }
 
        #endregion // Properties
 
        #region Methods
 
        /// <summary>
        /// This method must be called when the enumerator is no longer
        /// in use, if the last call to MoveNext returned true. The
        /// enumerator is no longer valid after this call.
        /// </summary>
        /// <remarks>
        /// When MoveNext returns false, the collection being enumerated is
        /// left in a good, known state, so nothing additional needs to be
        /// done. However, before MoveNext returns false the collection is
        /// in an intermediate state, so if the enumerator isn't advanced
        /// all the way to the end then this method must be called to clean
        /// up the collection and make sure that it ends in a known state.
        /// Calling this method in the case where MoveNext has returned
        /// false is OK. In that case, the method is a no-op.
        /// </remarks>
        internal void Dispose()
        {
            // Remove only those elements that we've previously seen.
            // This means removing the range between the read and write
            // indices, as that's dead space in the collection.
            if (_readIndex != _writeIndex)
            {
                _list.RemoveRange(_writeIndex, _readIndex - _writeIndex);
                _readIndex = _writeIndex = _list.Count;
            }
 
            _current = default(T);
 
#if DEBUG
            _valid = false;
#endif // DEBUG
        }
 
        /// <summary>
        /// Advances the enumerator to the next element of the collection.
        /// </summary>
        /// <returns>
        /// true if the enumerator was successfully advanced to the next
        /// element; false if the enumerator has passed the end of the
        /// collection.
        /// </returns>
        /// <remarks>
        /// Elements that have been GC'ed are removed from the list.
        /// </remarks>
        internal bool MoveNext()
        {
            // Get a reference to the next element that has not yet been
            // garbage-collected
            while (_readIndex < _list.Count)
            {
                WeakReference currentRef = _list[_readIndex];
                _current = (T)currentRef.Target;
                if ( (object)_current != null)
                {
                    // Found a live object. First compress the list, which
                    // is necessary if we've previously seen GC'ed objects.
                    if (_writeIndex != _readIndex)
                    {
                        _list[_writeIndex] = currentRef;
                    }
 
                    // Update internal state and return the found element
                    _readIndex++;
                    _writeIndex++;
 
#if DEBUG
                    _valid = true;
#endif // DEBUG
 
                    return true;
                }
                else
                {
                    // This object was garbage-collected, so keep looking
                    _readIndex++;
                }
            }
 
            // If we get here we didn't have any more live elements in the
            // collection, so we should return false. This is also a good
            // opportunity to clean up the list as necessary.
            Dispose();
 
            return false;
        }
 
 
        #endregion // Methods
 
        #region Data
 
        private List<WeakReference> _list;
        private T                   _current;
        private int                 _readIndex;
        private int                 _writeIndex;
 
#if DEBUG
        private bool                _valid;
#endif // DEBUG
 
        #endregion // Data
 
        #endregion // Internal implementation
    }
}