IndexedEnumerable.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Data / IndexedEnumerable.cs / 1 / IndexedEnumerable.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) 2003 by Microsoft Corporation.  All rights reserved.
//  
//
// 
// Description: offers optimistic indexer, i.e. this[int index] { get; } 
//      for a collection implementing IEnumerable, assuming that after an initial request
//      to read item[N], the following indices will be a sequence for index N+1, N+2 etc. 
//
// History:
//  08/19/2003 : [....] - created
//  10/22/2004 : [....] - cached Count/IsEmpty/IndexOf added 
//
//--------------------------------------------------------------------------- 
using System; 
using System.Collections;
using System.Collections.Specialized; 
using System.ComponentModel;
using System.Reflection;

using System.Diagnostics; 
using System.Windows;
using System.Windows.Data; 
using MS.Utility; 

namespace MS.Internal.Data 
{
    /// 
    /// for a collection implementing IEnumerable this offers
    /// optimistic indexer, i.e. this[int index] { get; } 
    /// and cached Count/IsEmpty properties and IndexOf method,
    /// assuming that after an initial request to read item[N], 
    /// the following indices will be a sequence for index N+1, N+2 etc. 
    /// 
    ///  
    /// This class is NOT safe for multi-threaded use.
    /// if the source collection implements IList or ICollection, the corresponding
    /// properties/methods will be used instead of the cached versions
    ///  
    internal class IndexedEnumerable : IEnumerable, IWeakEventListener
    { 
 
        //-----------------------------------------------------
        // 
        //  Constructors
        //
        //-----------------------------------------------------
 
        /// 
        /// Initialize indexer with IEnumerable. 
        ///  
        internal IndexedEnumerable(IEnumerable collection)
            : this(collection, null) 
        {
        }

        ///  
        /// Initialize indexer with IEnumerable and collectionView for purpose of filtering.
        ///  
        internal IndexedEnumerable(IEnumerable collection, Predicate filterCallback) 
        {
            _filterCallback = filterCallback; 
            SetCollection(collection);

            // observe source collection for changes to invalidate enumerators
            // for IList we can get all information directly from the source collection, 
            // no need to track changes, no need to hook notification
            if (List == null) 
            { 
                INotifyCollectionChanged icc = collection as INotifyCollectionChanged;
                if (icc != null) 
                {
                    CollectionChangedEventManager.AddListener(icc, this);
                }
            } 
        }
 
 
        //------------------------------------------------------
        // 
        //  Internal Properties/Methods
        //
        //-----------------------------------------------------
 
        ///  Determines the index of a specific value in the collection. 
        ///if a FilterCallback is set, it will be reflected in the returned index 
        internal int IndexOf(object item) 
        {
            // try if source collection has a IndexOf method 
            int index;
            if (GetNativeIndexOf(item, out index))
            {
                return index; 
            }
 
            // if the enumerator is still valid, then we can 
            // just use the cached item.
            if (EnsureCacheCurrent()) 
            {
                if (item == _cachedItem)
                    return _cachedIndex;
            } 

            // If item != cached item, that doesn�t mean that the enumerator 
            // is `blown, it just means we have to go find the item represented by item. 
            index = -1;
            // only ask for fresh enumerator if current enumerator already was moved before 
            if (_cachedIndex >= 0)
            {
                // force a new enumerator
                UseNewEnumerator(); 
            }
            int i = 0; 
            while (_enumerator.MoveNext()) 
            {
                if (object.Equals(_enumerator.Current, item)) 
                {
                    index = i;
                    break;
                } 
                ++i;
            } 
 
            // update cache if item was found
            if (index >= 0) 
            {
                CacheCurrentItem(index, _enumerator.Current);
            }
            else 
            {
                // item not found and moved enumerator to end -> release it 
                ClearAllCaches(); 
                _enumerator = null;
            } 

            return index;
        }
 

        /// Gets the number of elements in the collection.  
        ///if a FilterCallback is set, it will be reflected in the returned Count 
        internal int Count
        { 
            get
            {
                // try if source collection has a Count property
                int count = 0; 
                if (GetNativeCount(out count))
                { 
                    return count; 
                }
 
                // use a previously calculated Count
                if (_cachedCount >= 0)
                {
                    return _cachedCount; 
                }
 
                // calculate and cache current Count value, using (filtered) enumerator 
                count = 0;
                IEnumerator ie = GetEnumerator(); 
                while (ie.MoveNext())
                {
                    ++count;
                } 

                _cachedCount = count; 
                _cachedIsEmpty = (_cachedCount == 0); 
                return count;
            } 
        }

        // cheaper than (Count == 0)
        internal bool IsEmpty 
        {
            get 
            { 
                // try if source collection has a IsEmpty property
                bool isEmpty; 
                if (GetNativeIsEmpty(out isEmpty))
                {
                    return isEmpty;
                } 

                if (_cachedIsEmpty.HasValue) 
                { 
                    return _cachedIsEmpty.Value;
                } 

                // determine using (filtered) enumerator
                IEnumerator ie = GetEnumerator();
                _cachedIsEmpty = !ie.MoveNext(); 

                if (_cachedIsEmpty.Value) 
                    _cachedCount = 0; 
                return _cachedIsEmpty.Value;
            } 
        }

        /// 
        ///     Indexer property to retrieve the item at the given 
        /// zero-based offset into the collection.
        ///  
        ///  
        /// Thrown if index is out of range
        ///  
        internal object this[int index]
        {
            get
            { 
#pragma warning disable 1634 // about to use PreSharp message numbers - unknown to C#
                // try if source collection has a indexer 
                object value; 
                if (GetNativeItemAt(index, out value))
                { 
                    return value;
                }

                if (index < 0) { 
#pragma warning suppress 6503   // "Property get methods should not throw exceptions."
                    throw new ArgumentOutOfRangeException("index"); // validating the index argument 
                } 

                int moveBy = (index - _cachedIndex); 
                if (moveBy < 0)
                {
                    // new index is before current position, need to reset enumerators
                    UseNewEnumerator(); // recreate a new enumerator, must not call .Reset anymore 
                    moveBy = index + 1; // force at least one MoveNext
                } 
 
                // if the enumerator is still valid, then we can
                // just use the cached value. 
                if (EnsureCacheCurrent())
                {
                    if (index == _cachedIndex)
                        return _cachedItem; 
                }
                else 
                { 
                    // there are new enumerators, caches were cleared, recalculate moveBy
                    moveBy = index + 1; // force at least one MoveNext 
                }

                // position enumerator at new index:
                while ((moveBy > 0) && _enumerator.MoveNext()) 
                {
                    moveBy--; 
                } 

                // moved beyond the end of the enumerator? 
                if (moveBy != 0) {
#pragma warning suppress 6503   // "Property get methods should not throw exceptions."
                    throw new ArgumentOutOfRangeException("index"); // validating the index argument
                } 

                CacheCurrentItem(index, _enumerator.Current); 
                return _cachedItem; 
#pragma warning restore 1634
            } 
        }

        /// 
        /// The enumerable collection wrapped by this indexer. 
        /// 
        internal IEnumerable Enumerable 
        { 
            get { return _enumerable; }
        } 

        /// 
        /// The collection wrapped by this indexer.
        ///  
        internal ICollection Collection
        { 
            get { return _collection; } 
        }
 
        /// 
        /// The list wrapped by this indexer.
        /// 
        internal IList List 
        {
            get { return _list; } 
        } 

        ///  
        /// The CollectionView wrapped by this indexer.
        /// 
        internal CollectionView CollectionView
        { 
            get { return _collectionView; }
        } 
 

        ///  Return an enumerator for the collection.  
        public IEnumerator GetEnumerator()
        {
            return new FilteredEnumerator(this, Enumerable, FilterCallback);
        } 

        /// 
        /// Copies all the elements of the current collection to the specified one-dimensional Array. 
        ///
        internal static void CopyTo(IEnumerable collection, Array array, int index) 
        {
            Invariant.Assert(collection != null, "collection is null");
            Invariant.Assert(array != null, "target array is null");
            Invariant.Assert(array.Rank == 1, "expected array of rank=1"); 
            Invariant.Assert(index >= 0, "index must be positive");
 
            ICollection ic = collection as ICollection; 
            if (ic != null)
            { 
                ic.CopyTo(array, index);
            }
            else
            { 
                IEnumerator e = collection.GetEnumerator();
                IList list = (IList) array; 
 
                while (e.MoveNext())
                { 
                    if (index < array.Length)
                    {
                        list[index] = e.Current;
                        ++index; 
                    }
                    else 
                    { 
                        // The number of elements in the source ICollection is greater than
                        // the available space from index to the end of the destination array. 
                        throw new ArgumentException(SR.Get(SRID.CopyToNotEnoughSpace), "index");
                    }
                }
            } 
        }
 
 
        internal void Invalidate()
        { 
            ClearAllCaches();

            // only track changes if source collection isn't already of type IList
            if (List == null) 
            {
                INotifyCollectionChanged icc = Enumerable as INotifyCollectionChanged; 
                if (icc != null) 
                {
                    CollectionChangedEventManager.RemoveListener(icc, this); 
                }
            }

            _enumerable = null; 
            _enumerator = null;
            _changeTracker = null; 
            _collection = null; 
            _list = null;
            _filterCallback = null; 
        }


        //------------------------------------------------------ 
        //
        //  Private Members 
        // 
        //------------------------------------------------------
 
        private Predicate FilterCallback
        {
            get
            { 
                return _filterCallback;
            } 
        } 

        private void CacheCurrentItem(int index, object item) 
        {
            _cachedIndex = index;
            _cachedItem = item;
            _cachedVersion = _enumeratorVersion; 
        }
 
        // checks and returns if cached values are still current 
        private bool EnsureCacheCurrent()
        { 
            int version = EnsureEnumerator();

            if (version != _cachedVersion)
            { 
                ClearAllCaches();
                _cachedVersion = version; 
            } 
            bool isCacheCurrent = (version == _cachedVersion) && (_cachedIndex >= 0);
 
#if DEBUG
            if (isCacheCurrent)
            {
                object current = null; 
                try
                { 
                    current = _enumerator.Current; 
                }
                catch (InvalidOperationException) 
                {
                    Debug.Assert(false, "EnsureCacheCurrent: _enumerator.Current failed with InvalidOperationException");
                }
                Debug.Assert(Object.Equals(_cachedItem, current), "EnsureCacheCurrent: _cachedItem out of sync with _enumerator.Current"); 
            }
#endif // DEBUG 
            return isCacheCurrent; 
        }
 

        // returns the current EnumeratorVersion to indicate
        // whether any cached values may be valid.
        private int EnsureEnumerator() 
        {
            if (_enumerator == null) 
            { 
                UseNewEnumerator();
            } 
            else
            {
                try
                { 
                    _changeTracker.MoveNext();
                } 
                catch (InvalidOperationException) 
                {
                    // collection was changed - start over with a new enumerator 
                    UseNewEnumerator();
                }
            }
 
            return _enumeratorVersion;
        } 
 
        private void UseNewEnumerator()
        { 
            // if _enumeratorVersion exceeds MaxValue, then it
            // will roll back to MinValue, and continue on from there.
            unchecked {++ _enumeratorVersion;}
 
            _changeTracker = _enumerable.GetEnumerator();
            _enumerator = GetEnumerator(); 
            _cachedIndex = -1;    // will force at least one MoveNext 
            _cachedItem = null;
        } 

        private void InvalidateEnumerator()
        {
            // if _enumeratorVersion exceeds MaxValue, then it 
            // will roll back to MinValue, and continue on from there.
            unchecked {++ _enumeratorVersion;} 
 
            _enumerator = null;
            ClearAllCaches(); 
        }

        private void ClearAllCaches()
        { 
            _cachedItem = null;
            _cachedIndex = -1; 
            _cachedCount = -1; 
        }
 
        private void SetCollection(IEnumerable collection)
        {
            Invariant.Assert(collection != null);
            _enumerable = collection; 
            _collection = collection as ICollection;
            _list = collection as IList; 
            _collectionView = collection as CollectionView; 

            // try finding Count, IndexOf and indexer members via reflection 
            if ((List == null) && (CollectionView == null))
            {
                Type srcType = collection.GetType();
                // try reflection for IndexOf(object) 
                MethodInfo mi = srcType.GetMethod("IndexOf", new Type[] {typeof(object)});
                if ((mi != null) && (mi.ReturnType == typeof(int))) 
                { 
                    _reflectedIndexOf = mi;
                } 

                // find matching indexer
                MemberInfo[] defaultMembers = srcType.GetDefaultMembers();
                for (int i = 0; i <= defaultMembers.Length - 1; i++) 
                {
                    PropertyInfo pi = defaultMembers[i] as PropertyInfo; 
                    if (pi != null) 
                    {
                        ParameterInfo[] indexerParameters = pi.GetIndexParameters(); 
                        if (indexerParameters.Length == 1)
                        {
                            if (indexerParameters[0].ParameterType.IsAssignableFrom(typeof(int)))
                            { 
                                _reflectedItemAt = pi;
                                break; 
                            } 
                        }
                    } 
                }

                if (Collection == null)
                { 
                    // try reflection for Count property
                    PropertyInfo pi = srcType.GetProperty("Count", typeof(int)); 
                    if (pi != null) 
                    {
                        _reflectedCount = pi; 
                    }
                }
            }
        } 

        // to avoid slower calculation walking a IEnumerable, 
        // try retreiving the requested value from source collection 
        // if it implements ICollection, IList or CollectionView
        private bool GetNativeCount(out int value) 
        {
            bool isNativeValue = false;
            value = -1;
            if (Collection != null) 
            {
                value = Collection.Count; 
                isNativeValue = true; 
            }
            else if (CollectionView != null) 
            {
                value = CollectionView.Count;
                isNativeValue = true;
            } 
            else if (_reflectedCount != null)
            { 
                try 
                {
                    value = (int) _reflectedCount.GetValue(Enumerable, null); 
                    isNativeValue = true;
                }
                catch (MethodAccessException )
                { 
                    // revert to walking the IEnumerable
                    // under partial trust, some properties are not accessible even though they are public 
                    // see bug 1415832 
                    _reflectedCount = null;
                    isNativeValue = false; 
                }
            }
            return isNativeValue;
        } 

        private bool GetNativeIsEmpty(out bool isEmpty) 
        { 
            bool isNativeValue = false;
            isEmpty = true; 
            if (Collection != null)
            {
                isEmpty = (Collection.Count == 0);
                isNativeValue = true; 
            }
            else if (CollectionView != null) 
            { 
                isEmpty= CollectionView.IsEmpty;
                isNativeValue = true; 
            }
            else if (_reflectedCount != null)
            {
                try 
                {
                    isEmpty = ((int)_reflectedCount.GetValue(Enumerable, null) == 0); 
                    isNativeValue = true; 
                }
                catch (MethodAccessException ) 
                {
                    // revert to walking the IEnumerable
                    // under partial trust, some properties are not accessible even though they are public
                    // see bug 1415832 
                    _reflectedCount = null;
                    isNativeValue = false; 
                } 
            }
            return isNativeValue; 
        }

        private bool GetNativeIndexOf(object item, out int value)
        { 
            bool isNativeValue = false;
            value = -1; 
            if ((List != null) && (FilterCallback == null)) 
            {
                value = List.IndexOf(item); 
                isNativeValue = true;
            }
            else if (CollectionView != null)
            { 
                value = CollectionView.IndexOf(item);
                isNativeValue = true; 
            } 
            else if (_reflectedIndexOf != null)
            { 
                try
                {
                    value = (int) _reflectedIndexOf.Invoke(Enumerable, new object[] { item });
                    isNativeValue = true; 
                }
                catch (MethodAccessException ) 
                { 
                    // revert to walking the IEnumerable
                    // under partial trust, some properties are not accessible even though they are public 
                    // see bug 1415832
                    _reflectedIndexOf = null;
                    isNativeValue = false;
                } 
            }
            return isNativeValue; 
        } 

        private bool GetNativeItemAt(int index, out object value) 
        {
            bool isNativeValue = false;
            value = null;
            if (List != null) 
            {
                value = List[index]; 
                isNativeValue = true; 
            }
            else if (CollectionView != null) 
            {
                value = CollectionView.GetItemAt(index);
                isNativeValue = true;
            } 
            else if (_reflectedItemAt!= null)
            { 
                try 
                {
                    value = _reflectedItemAt.GetValue(Enumerable, new object[] { index }); 
                    isNativeValue = true;
                }
                catch (MethodAccessException )
                { 
                    // revert to walking the IEnumerable
                    // under partial trust, some properties are not accessible even though they are public 
                    // see bug 1415832 
                    _reflectedItemAt = null;
                    isNativeValue = false; 
                }
            }
            return isNativeValue;
        } 

        //----------------------------------------------------- 
        // 
        //  Event handlers
        // 
        //------------------------------------------------------

        /// 
        /// Handle events from the centralized event table 
        /// 
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) 
        { 
            return ReceiveWeakEvent(managerType, sender, e);
        } 

        /// 
        /// Handle events from the centralized event table
        ///  
        protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        { 
            if (managerType == typeof(CollectionChangedEventManager)) 
            {
                InvalidateEnumerator(); 
            }
            else
            {
                return false;       // unrecognized event 
            }
 
            return true; 
        }
 
        //-----------------------------------------------------
        //
        //  Private Members
        // 
        //-----------------------------------------------------
 
        private IEnumerable _enumerable; 
        private IEnumerator _enumerator;
        private IEnumerator _changeTracker; 
        private ICollection _collection;
        private IList       _list;
        private CollectionView _collectionView;
 
        private int     _enumeratorVersion;
        private object  _cachedItem; 
        private int     _cachedIndex = -1; 
        private int     _cachedVersion = -1;
        private int     _cachedCount = -1; 
        private bool?   _cachedIsEmpty;

        private PropertyInfo    _reflectedCount;
        private PropertyInfo    _reflectedItemAt; 
        private MethodInfo      _reflectedIndexOf;
 
        private Predicate _filterCallback; 

 
        //-----------------------------------------------------
        //
        //  Private Types
        // 
        //------------------------------------------------------
        private class FilteredEnumerator : IEnumerator 
        { 
            public FilteredEnumerator(IndexedEnumerable indexedEnumerable, IEnumerable enumerable, Predicate filterCallback)
            { 
                _enumerable = enumerable;
                _enumerator = _enumerable.GetEnumerator();
                _filterCallback = filterCallback;
                _indexedEnumerable = indexedEnumerable; 
            }
 
            void IEnumerator.Reset() 
            {
                if (_indexedEnumerable._enumerable == null) 
                    throw new InvalidOperationException(SR.Get(SRID.EnumeratorVersionChanged));

                _enumerator = _enumerable.GetEnumerator();
            } 

            bool IEnumerator.MoveNext() 
            { 
                bool returnValue;
 
                if (_indexedEnumerable._enumerable == null)
                    throw new InvalidOperationException(SR.Get(SRID.EnumeratorVersionChanged));

                if (_filterCallback == null) 
                {
                    returnValue = _enumerator.MoveNext(); 
                } 
                else
                { 
                    while ( (returnValue = _enumerator.MoveNext()) && !_filterCallback(_enumerator.Current))
                        ;
                }
 
                return returnValue;
            } 
 
            object IEnumerator.Current
            { 
                get
                {
                    return _enumerator.Current;
                } 
            }
 
            IEnumerable _enumerable; 
            IEnumerator _enumerator;
            IndexedEnumerable _indexedEnumerable; 
            Predicate _filterCallback;
        }

    } 
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK